<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="generator" content="VuePress 2.0.0-beta.48" />
    <meta name="theme" content="VuePress Theme Hope" />
    <meta property="og:url" content="https://vuepress-theme-hope-v2-demo.mrhope.site/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html"><meta property="og:site_name" content="主题演示"><meta property="og:title" content="分布式"><meta property="og:type" content="article"><meta property="og:locale" content="zh-CN"><meta property="og:locale:alternate" content="en-US"><meta property="article:tag" content="分布式与高性能高可用"><title>分布式 | 主题演示</title><meta name="description" content="vuepress-theme-hope 的演示">
    <style>
      :root {
        --bg-color: #fff;
      }

      html[data-theme="dark"] {
        --bg-color: #1d2025;
      }

      html,
      body {
        background-color: var(--bg-color);
      }
    </style>
    <script>
      const userMode = localStorage.getItem("vuepress-theme-hope-scheme");
      const systemDarkMode =
        window.matchMedia &&
        window.matchMedia("(prefers-color-scheme: dark)").matches;

      if (userMode === "dark" || (userMode !== "light" && systemDarkMode)) {
        document.querySelector("html").setAttribute("data-theme", "dark");
      }
    </script>
    <link rel="stylesheet" href="/assets/style.dbbe025d.css">
    <link rel="modulepreload" href="/assets/app.e732cc94.js"><link rel="modulepreload" href="/assets/分布式与高性能高可用.html.a85c8cdb.js"><link rel="modulepreload" href="/assets/plugin-vue_export-helper.21dcd24c.js"><link rel="modulepreload" href="/assets/分布式与高性能高可用.html.38ab4746.js"><link rel="prefetch" href="/assets/home.html.d675c4b6.js"><link rel="prefetch" href="/assets/intro.html.684ef626.js"><link rel="prefetch" href="/assets/index.html.e8ffec66.js"><link rel="prefetch" href="/assets/slide.html.050a6f1e.js"><link rel="prefetch" href="/assets/disable.html.b507ecac.js"><link rel="prefetch" href="/assets/encrypt.html.762116d5.js"><link rel="prefetch" href="/assets/markdown.html.5acf7a9a.js"><link rel="prefetch" href="/assets/page.html.141b0147.js"><link rel="prefetch" href="/assets/index.html.75bf4c69.js"><link rel="prefetch" href="/assets/article10.html.f5ea4d77.js"><link rel="prefetch" href="/assets/article11.html.6339c214.js"><link rel="prefetch" href="/assets/article12.html.5ac86a8f.js"><link rel="prefetch" href="/assets/article9.html.666c37a0.js"><link rel="prefetch" href="/assets/home.html.6e361c52.js"><link rel="prefetch" href="/assets/index.html.2d9b62a7.js"><link rel="prefetch" href="/assets/slide.html.6b655fa0.js"><link rel="prefetch" href="/assets/article1.html.f835efef.js"><link rel="prefetch" href="/assets/article2.html.e4775195.js"><link rel="prefetch" href="/assets/article3.html.bfdf934f.js"><link rel="prefetch" href="/assets/article4.html.69bccee4.js"><link rel="prefetch" href="/assets/article5.html.c7ac5439.js"><link rel="prefetch" href="/assets/article6.html.880c0652.js"><link rel="prefetch" href="/assets/article7.html.2cae84db.js"><link rel="prefetch" href="/assets/article8.html.6c46837c.js"><link rel="prefetch" href="/assets/disable.html.f629efab.js"><link rel="prefetch" href="/assets/encrypt.html.58d61689.js"><link rel="prefetch" href="/assets/markdown.html.779c42bf.js"><link rel="prefetch" href="/assets/page.html.125d74f5.js"><link rel="prefetch" href="/assets/index.html.3db704fb.js"><link rel="prefetch" href="/assets/article10.html.b5d65d2d.js"><link rel="prefetch" href="/assets/article11.html.b2cf93ee.js"><link rel="prefetch" href="/assets/article12.html.94e1cf22.js"><link rel="prefetch" href="/assets/article9.html.90cf2a62.js"><link rel="prefetch" href="/assets/Docker.html.de1063e8.js"><link rel="prefetch" href="/assets/Java基础.html.6c0b0f5c.js"><link rel="prefetch" href="/assets/Java容器.html.77448ff5.js"><link rel="prefetch" href="/assets/JUC.html.76307785.js"><link rel="prefetch" href="/assets/JVM.html.0bb340ff.js"><link rel="prefetch" href="/assets/Linux.html.90ea1c3b.js"><link rel="prefetch" href="/assets/MySQL.html.73ae62bf.js"><link rel="prefetch" href="/assets/NIO与Netty.html.a73529b1.js"><link rel="prefetch" href="/assets/index.html.2a38d188.js"><link rel="prefetch" href="/assets/Redis.html.78427b11.js"><link rel="prefetch" href="/assets/主流框架.html.f21f66df.js"><link rel="prefetch" href="/assets/操作系统.html.76b600e3.js"><link rel="prefetch" href="/assets/智力题.html.ec368cec.js"><link rel="prefetch" href="/assets/系统设计.html.fb8f2074.js"><link rel="prefetch" href="/assets/计算机网络.html.1a7f7d0e.js"><link rel="prefetch" href="/assets/设计模式.html.cd48ccf5.js"><link rel="prefetch" href="/assets/article1.html.ca54de60.js"><link rel="prefetch" href="/assets/article2.html.a99d479f.js"><link rel="prefetch" href="/assets/article3.html.5a2797ee.js"><link rel="prefetch" href="/assets/article4.html.3ac5a509.js"><link rel="prefetch" href="/assets/article5.html.c09d20ba.js"><link rel="prefetch" href="/assets/article6.html.c9e59f52.js"><link rel="prefetch" href="/assets/article7.html.948e77cc.js"><link rel="prefetch" href="/assets/article8.html.28c3977f.js"><link rel="prefetch" href="/assets/404.html.8a909211.js"><link rel="prefetch" href="/assets/index.html.690b188a.js"><link rel="prefetch" href="/assets/index.html.6085d793.js"><link rel="prefetch" href="/assets/index.html.932078ca.js"><link rel="prefetch" href="/assets/index.html.bb629f30.js"><link rel="prefetch" href="/assets/index.html.92b2d70c.js"><link rel="prefetch" href="/assets/index.html.82840baf.js"><link rel="prefetch" href="/assets/index.html.85406aa8.js"><link rel="prefetch" href="/assets/index.html.7b9124b8.js"><link rel="prefetch" href="/assets/index.html.e1e2a548.js"><link rel="prefetch" href="/assets/index.html.a9a69593.js"><link rel="prefetch" href="/assets/index.html.5b51b72b.js"><link rel="prefetch" href="/assets/index.html.ec7f0295.js"><link rel="prefetch" href="/assets/index.html.e14e2286.js"><link rel="prefetch" href="/assets/index.html.e89216be.js"><link rel="prefetch" href="/assets/index.html.c0adb933.js"><link rel="prefetch" href="/assets/index.html.a45798a3.js"><link rel="prefetch" href="/assets/index.html.e99c516b.js"><link rel="prefetch" href="/assets/index.html.f6be7fb4.js"><link rel="prefetch" href="/assets/index.html.6812b391.js"><link rel="prefetch" href="/assets/index.html.4bae905e.js"><link rel="prefetch" href="/assets/index.html.0599248b.js"><link rel="prefetch" href="/assets/index.html.963b2596.js"><link rel="prefetch" href="/assets/index.html.bbd9778f.js"><link rel="prefetch" href="/assets/index.html.c6a7a8d4.js"><link rel="prefetch" href="/assets/index.html.13391f8f.js"><link rel="prefetch" href="/assets/index.html.4b0a0f3f.js"><link rel="prefetch" href="/assets/index.html.bbd06d0d.js"><link rel="prefetch" href="/assets/index.html.9a4d8710.js"><link rel="prefetch" href="/assets/index.html.dfdff039.js"><link rel="prefetch" href="/assets/index.html.cd21ad11.js"><link rel="prefetch" href="/assets/index.html.72b3c545.js"><link rel="prefetch" href="/assets/index.html.0262e65d.js"><link rel="prefetch" href="/assets/index.html.8905ca40.js"><link rel="prefetch" href="/assets/index.html.98e54f53.js"><link rel="prefetch" href="/assets/index.html.707c5120.js"><link rel="prefetch" href="/assets/index.html.daf3ede2.js"><link rel="prefetch" href="/assets/index.html.ae769b93.js"><link rel="prefetch" href="/assets/index.html.59a75e1b.js"><link rel="prefetch" href="/assets/index.html.efb1d101.js"><link rel="prefetch" href="/assets/index.html.898df6f8.js"><link rel="prefetch" href="/assets/index.html.6339ac7f.js"><link rel="prefetch" href="/assets/index.html.0ffdd311.js"><link rel="prefetch" href="/assets/index.html.454f52cf.js"><link rel="prefetch" href="/assets/index.html.d8a5ddde.js"><link rel="prefetch" href="/assets/index.html.3be06723.js"><link rel="prefetch" href="/assets/index.html.1d180388.js"><link rel="prefetch" href="/assets/index.html.337241b0.js"><link rel="prefetch" href="/assets/index.html.0be3cd3f.js"><link rel="prefetch" href="/assets/index.html.8e5505d4.js"><link rel="prefetch" href="/assets/index.html.ffdd1452.js"><link rel="prefetch" href="/assets/index.html.d3825f4c.js"><link rel="prefetch" href="/assets/home.html.ccae7996.js"><link rel="prefetch" href="/assets/intro.html.fd34d8e1.js"><link rel="prefetch" href="/assets/index.html.ba786069.js"><link rel="prefetch" href="/assets/slide.html.0df38877.js"><link rel="prefetch" href="/assets/disable.html.6465acbf.js"><link rel="prefetch" href="/assets/encrypt.html.f697c2c7.js"><link rel="prefetch" href="/assets/markdown.html.f534aa4c.js"><link rel="prefetch" href="/assets/page.html.d91d46f6.js"><link rel="prefetch" href="/assets/index.html.b66745bd.js"><link rel="prefetch" href="/assets/article10.html.a245f19a.js"><link rel="prefetch" href="/assets/article11.html.c7128e02.js"><link rel="prefetch" href="/assets/article12.html.abd25c27.js"><link rel="prefetch" href="/assets/article9.html.7bba6b9f.js"><link rel="prefetch" href="/assets/home.html.eb29b64c.js"><link rel="prefetch" href="/assets/index.html.a51898c1.js"><link rel="prefetch" href="/assets/slide.html.4db0d16b.js"><link rel="prefetch" href="/assets/article1.html.8aeaf420.js"><link rel="prefetch" href="/assets/article2.html.1b3af75b.js"><link rel="prefetch" href="/assets/article3.html.83bc62c6.js"><link rel="prefetch" href="/assets/article4.html.9c3e20ef.js"><link rel="prefetch" href="/assets/article5.html.a86dfd69.js"><link rel="prefetch" href="/assets/article6.html.1005b310.js"><link rel="prefetch" href="/assets/article7.html.e62a511b.js"><link rel="prefetch" href="/assets/article8.html.5d7e1f67.js"><link rel="prefetch" href="/assets/disable.html.9d48f34e.js"><link rel="prefetch" href="/assets/encrypt.html.69059b41.js"><link rel="prefetch" href="/assets/markdown.html.f8f89db4.js"><link rel="prefetch" href="/assets/page.html.f139635c.js"><link rel="prefetch" href="/assets/index.html.126e92a0.js"><link rel="prefetch" href="/assets/article10.html.f00bfbec.js"><link rel="prefetch" href="/assets/article11.html.4e4aea0e.js"><link rel="prefetch" href="/assets/article12.html.ed056ef8.js"><link rel="prefetch" href="/assets/article9.html.c3cb96a4.js"><link rel="prefetch" href="/assets/Docker.html.f45c96a5.js"><link rel="prefetch" href="/assets/Java基础.html.56267f30.js"><link rel="prefetch" href="/assets/Java容器.html.5139503a.js"><link rel="prefetch" href="/assets/JUC.html.ebe8f287.js"><link rel="prefetch" href="/assets/JVM.html.c448b42c.js"><link rel="prefetch" href="/assets/Linux.html.43840acf.js"><link rel="prefetch" href="/assets/MySQL.html.eba28d84.js"><link rel="prefetch" href="/assets/NIO与Netty.html.66d42188.js"><link rel="prefetch" href="/assets/index.html.f3cc61a2.js"><link rel="prefetch" href="/assets/Redis.html.d810a669.js"><link rel="prefetch" href="/assets/主流框架.html.48ca457c.js"><link rel="prefetch" href="/assets/操作系统.html.c2f0370c.js"><link rel="prefetch" href="/assets/智力题.html.4d751983.js"><link rel="prefetch" href="/assets/系统设计.html.c1542e9d.js"><link rel="prefetch" href="/assets/计算机网络.html.724e0903.js"><link rel="prefetch" href="/assets/设计模式.html.2465754d.js"><link rel="prefetch" href="/assets/article1.html.ac18bda1.js"><link rel="prefetch" href="/assets/article2.html.051f8867.js"><link rel="prefetch" href="/assets/article3.html.ebe7849b.js"><link rel="prefetch" href="/assets/article4.html.a4dbc976.js"><link rel="prefetch" href="/assets/article5.html.395a5403.js"><link rel="prefetch" href="/assets/article6.html.74c49cbf.js"><link rel="prefetch" href="/assets/article7.html.bf053e97.js"><link rel="prefetch" href="/assets/article8.html.7213c414.js"><link rel="prefetch" href="/assets/404.html.22aaa4fc.js"><link rel="prefetch" href="/assets/index.html.bd6ff77c.js"><link rel="prefetch" href="/assets/index.html.a7880d9d.js"><link rel="prefetch" href="/assets/index.html.f5f99f6a.js"><link rel="prefetch" href="/assets/index.html.561cd4cd.js"><link rel="prefetch" href="/assets/index.html.2b2206fe.js"><link rel="prefetch" href="/assets/index.html.ec6b7467.js"><link rel="prefetch" href="/assets/index.html.146ab8c4.js"><link rel="prefetch" href="/assets/index.html.f201f6e8.js"><link rel="prefetch" href="/assets/index.html.a1a6c5d4.js"><link rel="prefetch" href="/assets/index.html.61b33c23.js"><link rel="prefetch" href="/assets/index.html.8640e57f.js"><link rel="prefetch" href="/assets/index.html.c3d0fc1d.js"><link rel="prefetch" href="/assets/index.html.461f8cdf.js"><link rel="prefetch" href="/assets/index.html.1dea7bef.js"><link rel="prefetch" href="/assets/index.html.e6addda8.js"><link rel="prefetch" href="/assets/index.html.2638e7e9.js"><link rel="prefetch" href="/assets/index.html.5db2b8e8.js"><link rel="prefetch" href="/assets/index.html.cbff7fea.js"><link rel="prefetch" href="/assets/index.html.5ac21d40.js"><link rel="prefetch" href="/assets/index.html.c7acfbee.js"><link rel="prefetch" href="/assets/index.html.fb44feb1.js"><link rel="prefetch" href="/assets/index.html.d4e476c9.js"><link rel="prefetch" href="/assets/index.html.ec5764c9.js"><link rel="prefetch" href="/assets/index.html.d185c9f1.js"><link rel="prefetch" href="/assets/index.html.0867f7ab.js"><link rel="prefetch" href="/assets/index.html.2c3899c7.js"><link rel="prefetch" href="/assets/index.html.8a2aee35.js"><link rel="prefetch" href="/assets/index.html.130e4c85.js"><link rel="prefetch" href="/assets/index.html.7277b095.js"><link rel="prefetch" href="/assets/index.html.c074ddbd.js"><link rel="prefetch" href="/assets/index.html.e91a40c5.js"><link rel="prefetch" href="/assets/index.html.0b2b027d.js"><link rel="prefetch" href="/assets/index.html.6b602146.js"><link rel="prefetch" href="/assets/index.html.16d4af72.js"><link rel="prefetch" href="/assets/index.html.b8fc0dcc.js"><link rel="prefetch" href="/assets/index.html.9b3fcbd9.js"><link rel="prefetch" href="/assets/index.html.eeb89bf7.js"><link rel="prefetch" href="/assets/index.html.5114cd1e.js"><link rel="prefetch" href="/assets/index.html.b8354286.js"><link rel="prefetch" href="/assets/index.html.fcd579df.js"><link rel="prefetch" href="/assets/index.html.e743d320.js"><link rel="prefetch" href="/assets/index.html.b34f28e1.js"><link rel="prefetch" href="/assets/index.html.dd5dd927.js"><link rel="prefetch" href="/assets/index.html.7df41339.js"><link rel="prefetch" href="/assets/index.html.9de9b7ae.js"><link rel="prefetch" href="/assets/index.html.db158b25.js"><link rel="prefetch" href="/assets/index.html.fcab0b26.js"><link rel="prefetch" href="/assets/index.html.afada1db.js"><link rel="prefetch" href="/assets/index.html.8681058b.js"><link rel="prefetch" href="/assets/index.html.1c990d16.js"><link rel="prefetch" href="/assets/index.html.9563932e.js"><link rel="prefetch" href="/assets/404.4ba82410.js"><link rel="prefetch" href="/assets/Layout.67323a43.js"><link rel="prefetch" href="/assets/Slide.be972901.js"><link rel="prefetch" href="/assets/Blog.caaad5e4.js"><link rel="prefetch" href="/assets/giscus.es.63137db5.js"><link rel="prefetch" href="/assets/auto.esm.36809f22.js"><link rel="prefetch" href="/assets/index.daae04bf.js"><link rel="prefetch" href="/assets/index.1842ee54.js"><link rel="prefetch" href="/assets/mermaid.esm.min.ee1e0284.js"><link rel="prefetch" href="/assets/highlight.esm.d982e650.js"><link rel="prefetch" href="/assets/markdown.esm.832a189d.js"><link rel="prefetch" href="/assets/math.esm.a3f84b6f.js"><link rel="prefetch" href="/assets/notes.esm.3c361cb7.js"><link rel="prefetch" href="/assets/reveal.esm.b96f05d8.js"><link rel="prefetch" href="/assets/search.esm.80da4a02.js"><link rel="prefetch" href="/assets/zoom.esm.8514a202.js"><link rel="prefetch" href="/assets/photoswipe.esm.092fbc15.js">
  </head>
  <body>
    <div id="app"><!--[--><!--[--><!--[--><span tabindex="-1"></span><a href="#main-content" class="skip-link sr-only">Skip to content</a><!--]--><div class="theme-container has-toc"><!--[--><!--[--><header class="navbar"><div class="navbar-left"><button class="toggle-sidebar-button" title="Toggle Sidebar"><span class="icon"></span></button><!----><a href="/zh/" class="brand"><img class="logo" src="/logo.svg" alt="主题演示"><!----><span class="site-name hide-in-pad">主题演示</span></a><!----></div><div class="navbar-center"><!----><nav class="nav-links"><div class="nav-item hide-in-mobile"><a href="/zh/" class="nav-link" aria-label="博客主页"><span class="icon iconfont icon-home" style=""></span>博客主页<!----></a></div><div class="nav-item hide-in-mobile"><a href="/zh/home.html" class="nav-link" aria-label="项目主页"><span class="icon iconfont icon-home" style=""></span>项目主页<!----></a></div><div class="nav-item hide-in-mobile"><a href="/zh/guide/" class="nav-link" aria-label="使用指南"><span class="icon iconfont icon-creative" style=""></span>使用指南<!----></a></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="博文"><span class="title"><span class="icon iconfont icon-edit" style=""></span>博文</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><h4 class="dropdown-subtitle"><span>文章 1-4</span></h4><ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/zh/posts/article/article1.html" class="nav-link" aria-label="文章 1"><span class="icon iconfont icon-edit" style=""></span>文章 1<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article2.html" class="nav-link" aria-label="文章 2"><span class="icon iconfont icon-edit" style=""></span>文章 2<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article3.html" class="nav-link" aria-label="文章 3"><span class="icon iconfont icon-edit" style=""></span>文章 3<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article4.html" class="nav-link" aria-label="文章 4"><span class="icon iconfont icon-edit" style=""></span>文章 4<!----></a></li></ul></li><li class="dropdown-item"><h4 class="dropdown-subtitle"><span>文章 5-12</span></h4><ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/zh/posts/article/article5.html" class="nav-link" aria-label="文章 5"><span class="icon iconfont icon-edit" style=""></span>文章 5<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article6.html" class="nav-link" aria-label="文章 6"><span class="icon iconfont icon-edit" style=""></span>文章 6<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article7.html" class="nav-link" aria-label="文章 7"><span class="icon iconfont icon-edit" style=""></span>文章 7<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article8.html" class="nav-link" aria-label="文章 8"><span class="icon iconfont icon-edit" style=""></span>文章 8<!----></a></li></ul></li><li class="dropdown-item"><a href="/zh/posts/article9.html" class="nav-link" aria-label="文章 9"><span class="icon iconfont icon-edit" style=""></span>文章 9<!----></a></li><li class="dropdown-item"><a href="/zh/posts/article10.html" class="nav-link" aria-label="文章 10"><span class="icon iconfont icon-edit" style=""></span>文章 10<!----></a></li><li class="dropdown-item"><a href="/zh/posts/article11.html" class="nav-link" aria-label="文章 11"><span class="icon iconfont icon-edit" style=""></span>文章 11<!----></a></li><li class="dropdown-item"><a href="/zh/posts/article12.html" class="nav-link" aria-label="文章 12"><span class="icon iconfont icon-edit" style=""></span>文章 12<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><a href="https://vuepress-theme-hope.github.io/v2/zh/" rel="noopener noreferrer" target="_blank" aria-label="主题文档" class="nav-link"><span class="icon iconfont icon-note" style=""></span>主题文档<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span><!----></a></div></nav><!----></div><div class="navbar-right"><!----><div class="nav-item"><div class="dropdown-wrapper i18n-dropdown"><button class="dropdown-title" type="button" aria-label="选择语言"><!--[--><svg xmlns="http://www.w3.org/2000/svg" class="icon i18n-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="i18n icon" style="width:1rem;height:1rem;vertical-align:middle;"><path d="M379.392 460.8 494.08 575.488l-42.496 102.4L307.2 532.48 138.24 701.44l-71.68-72.704L234.496 460.8l-45.056-45.056c-27.136-27.136-51.2-66.56-66.56-108.544h112.64c7.68 14.336 16.896 27.136 26.112 35.84l45.568 46.08 45.056-45.056C382.976 312.32 409.6 247.808 409.6 204.8H0V102.4h256V0h102.4v102.4h256v102.4H512c0 70.144-37.888 161.28-87.04 210.944L378.88 460.8zM576 870.4 512 1024H409.6l256-614.4H768l256 614.4H921.6l-64-153.6H576zM618.496 768h196.608L716.8 532.48 618.496 768z"></path></svg><!--]--><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/" class="nav-link" aria-label="English"><!---->English<!----></a></li><li class="dropdown-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html" class="router-link-active router-link-exact-active nav-link active" aria-label="简体中文"><!---->简体中文<!----></a></li></ul></button></div></div><div class="nav-item"><a class="repo-link" href="https://gitee.com/changluJava/personal-reading" target="_blank" rel="noopener noreferrer" aria-label="Gitee"><svg xmlns="http://www.w3.org/2000/svg" class="icon gitee-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="gitee icon" style="width:1.25rem;height:1.25rem;vertical-align:middle;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm242.97-533.34H482.39a23.7 23.7 0 0 0-23.7 23.7l-.03 59.28c0 13.08 10.59 23.7 23.7 23.7h165.96a23.7 23.7 0 0 1 23.7 23.7v11.85a71.1 71.1 0 0 1-71.1 71.1H375.71a23.7 23.7 0 0 1-23.7-23.7V423.11a71.1 71.1 0 0 1 71.1-71.1h331.8a23.7 23.7 0 0 0 23.7-23.7l.06-59.25a23.73 23.73 0 0 0-23.7-23.73H423.11a177.78 177.78 0 0 0-177.78 177.75v331.83c0 13.08 10.62 23.7 23.7 23.7h349.62a159.99 159.99 0 0 0 159.99-159.99V482.33a23.7 23.7 0 0 0-23.7-23.7z"></path></svg></a></div><div class="nav-item hide-in-mobile"><button id="appearance-switch"><svg xmlns="http://www.w3.org/2000/svg" class="icon auto-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="auto icon" style="display:block;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="dark icon" style="display:none;"><path d="M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon light-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="light icon" style="display:none;"><path d="M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"></path></svg></button></div><!----><!----><button class="toggle-navbar-button" aria-label="Toggle Navbar" aria-expanded="false" aria-controls="nav-screen"><span class="button-container"><span class="button-top"></span><span class="button-middle"></span><span class="button-bottom"></span></span></button></div></header><!----><!--]--><!----><div class="toggle-sidebar-wrapper"><span class="arrow left"></span></div><aside class="sidebar"><!--[--><!----><!--]--><ul class="sidebar-links"><li><!--[--><a href="/zh/" class="nav-link sidebar-link sidebar-page" aria-label="博客主页"><span class="icon iconfont icon-home" style=""></span>博客主页<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/home.html" class="nav-link sidebar-link sidebar-page" aria-label="项目主页"><span class="icon iconfont icon-home" style=""></span>项目主页<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/slide" class="nav-link sidebar-link sidebar-page" aria-label="幻灯片"><!---->幻灯片<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><section class="sidebar-group"><button class="sidebar-heading clickable active"><span class="icon iconfont icon-creative" style=""></span><a href="/zh/interview/" class="title">面试指南</a><span class="arrow down"></span></button><ul class="sidebar-links"><li><!--[--><a href="/zh/interview/Docker.html" class="nav-link sidebar-link sidebar-page" aria-label="Docker"><span class="icon iconfont icon-lock" style=""></span>Docker<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/Java%E5%9F%BA%E7%A1%80.html" class="nav-link sidebar-link sidebar-page" aria-label="JAVA"><span class="icon iconfont icon-lock" style=""></span>JAVA<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/Java%E5%AE%B9%E5%99%A8.html" class="nav-link sidebar-link sidebar-page" aria-label="Java容器"><span class="icon iconfont icon-lock" style=""></span>Java容器<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/JUC.html" class="nav-link sidebar-link sidebar-page" aria-label="JUC"><span class="icon iconfont icon-lock" style=""></span>JUC<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/JVM.html" class="nav-link sidebar-link sidebar-page" aria-label="JVM"><span class="icon iconfont icon-lock" style=""></span>JVM<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/Linux.html" class="nav-link sidebar-link sidebar-page" aria-label="Linux"><span class="icon iconfont icon-lock" style=""></span>Linux<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/MySQL.html" class="nav-link sidebar-link sidebar-page" aria-label="MySQL"><span class="icon iconfont icon-lock" style=""></span>MySQL<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/NIO%E4%B8%8ENetty.html" class="nav-link sidebar-link sidebar-page" aria-label="Netty"><span class="icon iconfont icon-lock" style=""></span>Netty<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/Redis.html" class="nav-link sidebar-link sidebar-page" aria-label="Redis"><span class="icon iconfont icon-lock" style=""></span>Redis<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6.html" class="nav-link sidebar-link sidebar-page" aria-label="主流框架"><span class="icon iconfont icon-lock" style=""></span>主流框架<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html" class="router-link-active router-link-exact-active nav-link active sidebar-link sidebar-page active" aria-label="分布式"><span class="icon iconfont icon-lock" style=""></span>分布式<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、cap理论" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、CAP理论"><!---->1、CAP理论<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、cap简介" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、CAP简介"><!---->1、CAP简介<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、不是所谓的-3-选-2" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、不是所谓的“3 选 2”"><!---->2、不是所谓的“3 选 2”<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、cap-实际应用案例" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、CAP 实际应用案例"><!---->3、CAP 实际应用案例<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#总结" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="总结"><!---->总结<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、base理论-ap补充延申" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、Base理论（AP补充延申）"><!---->2、Base理论（AP补充延申）<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、base-理论的核心思想" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、BASE 理论的核心思想"><!---->1、BASE 理论的核心思想<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#总结-1" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="总结"><!---->总结<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、paxos-算法-分布式系统共识" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、Paxos 算法（分布式系统共识）"><!---->3、Paxos 算法（分布式系统共识）<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、认识paxos算法" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、认识Paxos算法"><!---->1、认识Paxos算法<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、basic-paxos-算法" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、Basic Paxos 算法"><!---->2、Basic Paxos 算法<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、multi-paxos-思想" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、Multi Paxos 思想"><!---->3、Multi Paxos 思想<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_4、raft-算法-分布式共识" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、Raft 算法（分布式共识）"><!---->4、Raft 算法（分布式共识）<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#背景" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="背景"><!---->背景<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、拜占庭将军" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、拜占庭将军"><!---->1、拜占庭将军<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、共识算法" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、共识算法"><!---->2、共识算法<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、基础" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、基础"><!---->3、基础<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_4、领导人选举" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、领导人选举"><!---->4、领导人选举<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_5、日志复制" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5、日志复制"><!---->5、日志复制<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_6、安全性" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="6、安全性"><!---->6、安全性<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_5、网关" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5、网关"><!---->5、网关<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、何为网关-为什么要网关" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、何为网关？为什么要网关？"><!---->1、何为网关？为什么要网关？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、有哪些常见的网关系统" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、有哪些常见的网关系统？"><!---->2、有哪些常见的网关系统？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_6、分布式id" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="6、分布式ID"><!---->6、分布式ID<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、分布式-id-介绍" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、分布式 ID 介绍"><!---->1、分布式 ID 介绍<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、-分布式-id-常见解决方案" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、 分布式 ID 常见解决方案"><!---->2、 分布式 ID 常见解决方案<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#分布式-id-生成方案总结" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="分布式 ID 生成方案总结"><!---->分布式 ID 生成方案总结<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_7、rpc" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="7、RPC"><!---->7、RPC<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、rpc基础" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、RPC基础"><!---->1、RPC基础<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、rpc-的原理是什么" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、RPC 的原理是什么?"><!---->2、RPC 的原理是什么?<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#dubbo知识点-面试题总结" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="Dubbo知识点&amp;面试题总结"><!---->Dubbo知识点&amp;面试题总结<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#服务之间的调用为啥不直接用-http-而用-rpc" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="服务之间的调用为啥不直接用 HTTP 而用 RPC？"><!---->服务之间的调用为啥不直接用 HTTP 而用 RPC？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_8、分布式事务" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="8、分布式事务"><!---->8、分布式事务<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、基础理论" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、基础理论"><!---->1、基础理论<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、分布式事务解决方案" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、分布式事务解决方案"><!---->2、分布式事务解决方案<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_9、分布式协调" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="9、分布式协调"><!---->9、分布式协调<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#zookeeper" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="Zookeeper"><!---->Zookeeper<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#读写分离" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="读写分离"><!---->读写分离<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、产生的问题以及解决方案" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、产生的问题以及解决方案"><!---->1、产生的问题以及解决方案<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、-如何实现读写分离" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、 如何实现读写分离？"><!---->2、 如何实现读写分离？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、主从复制原理了解么" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、主从复制原理了解么？"><!---->3、主从复制原理了解么？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#总结-3" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="总结"><!---->总结<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#分库分表" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="分库分表"><!---->分库分表<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、理解分库分表" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、理解分库分表"><!---->1、理解分库分表<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、什么情况下需要分库分表" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、什么情况下需要分库分表？"><!---->2、什么情况下需要分库分表？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、分库分表会带来什么问题呢" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、分库分表会带来什么问题呢？"><!---->3、分库分表会带来什么问题呢？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_4、分库分表有没有什么比较推荐的方案" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、分库分表有没有什么比较推荐的方案？"><!---->4、分库分表有没有什么比较推荐的方案？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_5、分库分表后-数据怎么迁移呢" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5、分库分表后，数据怎么迁移呢？"><!---->5、分库分表后，数据怎么迁移呢？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#负载均衡" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="负载均衡"><!---->负载均衡<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#消息队列" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="消息队列"><!---->消息队列<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、什么是消息队列" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、什么是消息队列？"><!---->1、什么是消息队列？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、为什么要用消息队列" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、为什么要用消息队列？"><!---->2、为什么要用消息队列？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、使用消息队列带来的一些问题" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、使用消息队列带来的一些问题"><!---->3、使用消息队列带来的一些问题<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_4、jms-vs-amqp" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、JMS VS AMQP"><!---->4、JMS VS AMQP<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_5、常见的消息队列机制" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5、常见的消息队列机制"><!---->5、常见的消息队列机制<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#总结-4" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="总结"><!---->总结<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#性能测试" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="性能测试"><!---->性能测试<!----></a><ul class="sidebar-sub-headers"></ul></li></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F.html" class="nav-link sidebar-link sidebar-page" aria-label="操作系统"><span class="icon iconfont icon-lock" style=""></span>操作系统<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E6%99%BA%E5%8A%9B%E9%A2%98.html" class="nav-link sidebar-link sidebar-page" aria-label="智力题"><span class="icon iconfont icon-lock" style=""></span>智力题<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1.html" class="nav-link sidebar-link sidebar-page" aria-label="系统设计"><span class="icon iconfont icon-lock" style=""></span>系统设计<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.html" class="nav-link sidebar-link sidebar-page" aria-label="计算机网络"><span class="icon iconfont icon-lock" style=""></span>计算机网络<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html" class="nav-link sidebar-link sidebar-page" aria-label="设计模式"><!---->设计模式<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section><!--]--></li><li><!--[--><section class="sidebar-group"><p class="sidebar-heading clickable"><span class="icon iconfont icon-creative" style=""></span><a href="/zh/guide/" class="title">如何使用</a><!----></p><ul class="sidebar-links"><li><!--[--><a href="/zh/guide/page.html" class="nav-link sidebar-link sidebar-page" aria-label="页面配置"><span class="icon iconfont icon-page" style=""></span>页面配置<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/guide/markdown.html" class="nav-link sidebar-link sidebar-page" aria-label="Markdown 展示"><span class="icon iconfont icon-markdown" style=""></span>Markdown 展示<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/guide/disable.html" class="nav-link sidebar-link sidebar-page" aria-label="布局与功能禁用"><span class="icon iconfont icon-config" style=""></span>布局与功能禁用<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/guide/encrypt.html" class="nav-link sidebar-link sidebar-page" aria-label="密码加密的文章"><span class="icon iconfont icon-lock" style=""></span>密码加密的文章<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section><!--]--></li><li><!--[--><section class="sidebar-group"><p class="sidebar-heading"><span class="icon iconfont icon-note" style=""></span><span class="title">文章</span><!----></p><ul class="sidebar-links"><li><!--[--><section class="sidebar-group"><button class="sidebar-heading clickable"><span class="icon iconfont icon-note" style=""></span><span class="title">文章 1-4</span><span class="arrow right"></span></button><!----></section><!--]--></li><li><!--[--><section class="sidebar-group"><p class="sidebar-heading"><span class="icon iconfont icon-note" style=""></span><span class="title">文章 5-12</span><!----></p><ul class="sidebar-links"><li><!--[--><section class="sidebar-group"><button class="sidebar-heading clickable"><span class="icon iconfont icon-note" style=""></span><span class="title">文章 5-8</span><span class="arrow right"></span></button><!----></section><!--]--></li><li><!--[--><section class="sidebar-group"><p class="sidebar-heading"><span class="icon iconfont icon-note" style=""></span><span class="title">文章 9-12</span><!----></p><ul class="sidebar-links"><li><!--[--><a href="/zh/posts/article9.html" class="nav-link sidebar-link sidebar-page" aria-label="文章 9"><span class="icon iconfont icon-edit" style=""></span>文章 9<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/posts/article10.html" class="nav-link sidebar-link sidebar-page" aria-label="文章 10"><span class="icon iconfont icon-edit" style=""></span>文章 10<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/posts/article11.html" class="nav-link sidebar-link sidebar-page" aria-label="文章 11"><span class="icon iconfont icon-edit" style=""></span>文章 11<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/posts/article12.html" class="nav-link sidebar-link sidebar-page" aria-label="文章 12"><span class="icon iconfont icon-edit" style=""></span>文章 12<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section><!--]--></li></ul></section><!--]--></li></ul></section><!--]--></li></ul><!--[--><!----><!--]--></aside><!--[--><main class="page" id="main-content"><!--[--><!----><nav class="breadcrumb disable"></nav><div class="page-title"><h1><span class="icon iconfont icon-lock" style=""></span>分布式</h1><div class="page-info"><span class="author-info" aria-label="作者🖊" data-balloon-pos="down" isoriginal="false" pageview="false"><svg xmlns="http://www.w3.org/2000/svg" class="icon author-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="author icon"><path d="M649.6 633.6c86.4-48 147.2-144 147.2-249.6 0-160-128-288-288-288s-288 128-288 288c0 108.8 57.6 201.6 147.2 249.6-121.6 48-214.4 153.6-240 288-3.2 9.6 0 19.2 6.4 25.6 3.2 9.6 12.8 12.8 22.4 12.8h704c9.6 0 19.2-3.2 25.6-12.8 6.4-6.4 9.6-16 6.4-25.6-25.6-134.4-121.6-240-243.2-288z"></path></svg><span><a class="author-item" href="https://changlu.blog.csdn.net" target="_blank" rel="noopener noreferrer">长路</a></span><span property="author" content="长路"></span></span><!----><!----><span class="category-info" aria-label="分类🌈" data-balloon-pos="down" isoriginal="false" pageview="false"><svg xmlns="http://www.w3.org/2000/svg" class="icon category-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="category icon"><path d="M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"></path></svg><ul class="categories-wrapper"><li class="category category8 clickable" role="navigation">面试题</li><meta property="articleSection" content="面试题"></ul></span><span aria-label="标签🏷" data-balloon-pos="down" isoriginal="false" pageview="false"><svg xmlns="http://www.w3.org/2000/svg" class="icon tag-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="tag icon"><path d="M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"></path></svg><ul class="tags-wrapper"><li class="tag tag7 clickable" role="navigation">分布式与高性能高可用</li></ul><meta property="keywords" content="分布式与高性能高可用"></span><span class="reading-time-info" aria-label="阅读时间⌛" data-balloon-pos="down" isoriginal="false" pageview="false"><svg xmlns="http://www.w3.org/2000/svg" class="icon timer-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="timer icon"><path d="M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"></path></svg><span>大约 94 分钟</span><meta property="timeRequired" content="PT94M"></span></div><hr></div><div class="toc-place-holder"><aside id="toc"><div class="toc-header">此页内容</div><div class="toc-wrapper"><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、cap理论" class="router-link-active router-link-exact-active toc-link level2">1、CAP理论</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、cap简介" class="router-link-active router-link-exact-active toc-link level3">1、CAP简介</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、不是所谓的-3-选-2" class="router-link-active router-link-exact-active toc-link level3">2、不是所谓的“3 选 2”</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、cap-实际应用案例" class="router-link-active router-link-exact-active toc-link level3">3、CAP 实际应用案例</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#总结" class="router-link-active router-link-exact-active toc-link level3">总结</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、base理论-ap补充延申" class="router-link-active router-link-exact-active toc-link level2">2、Base理论（AP补充延申）</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、base-理论的核心思想" class="router-link-active router-link-exact-active toc-link level3">1、BASE 理论的核心思想</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#总结-1" class="router-link-active router-link-exact-active toc-link level3">总结</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、paxos-算法-分布式系统共识" class="router-link-active router-link-exact-active toc-link level2">3、Paxos 算法（分布式系统共识）</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、认识paxos算法" class="router-link-active router-link-exact-active toc-link level3">1、认识Paxos算法</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、basic-paxos-算法" class="router-link-active router-link-exact-active toc-link level3">2、Basic Paxos 算法</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、multi-paxos-思想" class="router-link-active router-link-exact-active toc-link level3">3、Multi Paxos 思想</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_4、raft-算法-分布式共识" class="router-link-active router-link-exact-active toc-link level2">4、Raft 算法（分布式共识）</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#背景" class="router-link-active router-link-exact-active toc-link level3">背景</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、拜占庭将军" class="router-link-active router-link-exact-active toc-link level3">1、拜占庭将军</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、共识算法" class="router-link-active router-link-exact-active toc-link level3">2、共识算法</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、基础" class="router-link-active router-link-exact-active toc-link level3">3、基础</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_4、领导人选举" class="router-link-active router-link-exact-active toc-link level3">4、领导人选举</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_5、日志复制" class="router-link-active router-link-exact-active toc-link level3">5、日志复制</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_6、安全性" class="router-link-active router-link-exact-active toc-link level3">6、安全性</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_5、网关" class="router-link-active router-link-exact-active toc-link level2">5、网关</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、何为网关-为什么要网关" class="router-link-active router-link-exact-active toc-link level3">1、何为网关？为什么要网关？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、有哪些常见的网关系统" class="router-link-active router-link-exact-active toc-link level3">2、有哪些常见的网关系统？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_6、分布式id" class="router-link-active router-link-exact-active toc-link level2">6、分布式ID</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、分布式-id-介绍" class="router-link-active router-link-exact-active toc-link level3">1、分布式 ID 介绍</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、-分布式-id-常见解决方案" class="router-link-active router-link-exact-active toc-link level3">2、 分布式 ID 常见解决方案</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#分布式-id-生成方案总结" class="router-link-active router-link-exact-active toc-link level3">分布式 ID 生成方案总结</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_7、rpc" class="router-link-active router-link-exact-active toc-link level2">7、RPC</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、rpc基础" class="router-link-active router-link-exact-active toc-link level3">1、RPC基础</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、rpc-的原理是什么" class="router-link-active router-link-exact-active toc-link level3">2、RPC 的原理是什么?</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#dubbo知识点-面试题总结" class="router-link-active router-link-exact-active toc-link level3">Dubbo知识点&amp;面试题总结</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#服务之间的调用为啥不直接用-http-而用-rpc" class="router-link-active router-link-exact-active toc-link level3">服务之间的调用为啥不直接用 HTTP 而用 RPC？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_8、分布式事务" class="router-link-active router-link-exact-active toc-link level2">8、分布式事务</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、基础理论" class="router-link-active router-link-exact-active toc-link level3">1、基础理论</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、分布式事务解决方案" class="router-link-active router-link-exact-active toc-link level3">2、分布式事务解决方案</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_9、分布式协调" class="router-link-active router-link-exact-active toc-link level2">9、分布式协调</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#zookeeper" class="router-link-active router-link-exact-active toc-link level3">Zookeeper</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#读写分离" class="router-link-active router-link-exact-active toc-link level2">读写分离</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、产生的问题以及解决方案" class="router-link-active router-link-exact-active toc-link level3">1、产生的问题以及解决方案</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、-如何实现读写分离" class="router-link-active router-link-exact-active toc-link level3">2、 如何实现读写分离？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、主从复制原理了解么" class="router-link-active router-link-exact-active toc-link level3">3、主从复制原理了解么？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#总结-3" class="router-link-active router-link-exact-active toc-link level3">总结</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#分库分表" class="router-link-active router-link-exact-active toc-link level2">分库分表</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、理解分库分表" class="router-link-active router-link-exact-active toc-link level3">1、理解分库分表</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、什么情况下需要分库分表" class="router-link-active router-link-exact-active toc-link level3">2、什么情况下需要分库分表？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、分库分表会带来什么问题呢" class="router-link-active router-link-exact-active toc-link level3">3、分库分表会带来什么问题呢？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_4、分库分表有没有什么比较推荐的方案" class="router-link-active router-link-exact-active toc-link level3">4、分库分表有没有什么比较推荐的方案？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_5、分库分表后-数据怎么迁移呢" class="router-link-active router-link-exact-active toc-link level3">5、分库分表后，数据怎么迁移呢？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#负载均衡" class="router-link-active router-link-exact-active toc-link level2">负载均衡</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#消息队列" class="router-link-active router-link-exact-active toc-link level2">消息队列</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_1、什么是消息队列" class="router-link-active router-link-exact-active toc-link level3">1、什么是消息队列？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_2、为什么要用消息队列" class="router-link-active router-link-exact-active toc-link level3">2、为什么要用消息队列？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_3、使用消息队列带来的一些问题" class="router-link-active router-link-exact-active toc-link level3">3、使用消息队列带来的一些问题</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_4、jms-vs-amqp" class="router-link-active router-link-exact-active toc-link level3">4、JMS VS AMQP</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#_5、常见的消息队列机制" class="router-link-active router-link-exact-active toc-link level3">5、常见的消息队列机制</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#总结-4" class="router-link-active router-link-exact-active toc-link level3">总结</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html#性能测试" class="router-link-active router-link-exact-active toc-link level2">性能测试</a></li><!----><!--]--></ul></div></aside></div><!----><div class="theme-hope-content"><h1 id="分布式" tabindex="-1"><a class="header-anchor" href="#分布式" aria-hidden="true">#</a> 分布式</h1><h2 id="_1、cap理论" tabindex="-1"><a class="header-anchor" href="#_1、cap理论" aria-hidden="true">#</a> 1、CAP理论</h2><h3 id="_1、cap简介" tabindex="-1"><a class="header-anchor" href="#_1、cap简介" aria-hidden="true">#</a> 1、CAP简介</h3><p>CAP定理又被称作 <strong>布鲁尔定理（Brewer’s theorem）</strong>。2年后，麻省理工学院的Seth Gilbert和Nancy Lynch 发表了布鲁尔猜想的证明，CAP理论正式成为分布式领域的定理。</p><p><strong>CAP</strong> 也就是 <strong>Consistency（一致性）</strong>、<strong>Availability（可用性）</strong>、<strong>Partition Tolerance（分区容错性）</strong></p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200854764.png" alt="image-20220620085420583" loading="lazy"></p><p>在理论计算机科学中，CAP 定理（CAP theorem）指出对于一个分布式系统来说，当设计读写操作时，只能同时满足以下三点中的两个：</p><ul><li><strong>一致性（Consistency）</strong> : 所有节点访问同一份最新的数据副本</li><li><strong>可用性（Availability）</strong>: 非故障的节点在合理的时间内返回合理的响应（不是错误或者超时的响应）。</li><li><strong>分区容错性（Partition tolerance）</strong> : 分布式系统出现网络分区的时候，仍然能够对外提供服务。</li></ul><p><strong>什么是网络分区？</strong></p><ul><li>分布式系统中，多个节点之前的网络本来是连通的，但是因为某些故障（比如部分节点网络出了问题）某些节点之间不连通了，整个网络就分成了几块区域，这就叫网络分区。</li></ul><h3 id="_2、不是所谓的-3-选-2" tabindex="-1"><a class="header-anchor" href="#_2、不是所谓的-3-选-2" aria-hidden="true">#</a> 2、不是所谓的“3 选 2”</h3><p>大部分人解释这一定律时，常常简单的表述为：“一致性、可用性、分区容忍性三者你只能同时达到其中两个，不可能同时达到”。实际上这是一个非常具有误导性质的说法，而且在 CAP 理论诞生 12 年之后，CAP 之父也在 2012 年重写了之前的论文。</p><blockquote><p><strong>当发生网络分区的时候，如果我们要继续服务，那么强一致性和可用性只能 2 选 1。也就是说当网络分区之后 P 是前提，决定了 P 之后才有 C 和 A 的选择。也就是说分区容错性（Partition tolerance）我们是必须要实现的。</strong></p><p>简而言之就是：CAP 理论中分区容错性 P 是一定要满足的，在此基础上，只能满足可用性 A 或者一致性 C。</p></blockquote><p><strong>分布式系统理论上不可能选择 CA 架构，只能选择 CP 或者 AP 架构。</strong> 比如 ZooKeeper、HBase 就是 CP 架构，Cassandra、Eureka 就是 AP 架构，Nacos 不仅支持 CP 架构也支持 AP 架构。</p><p><strong>为啥不可能选择 CA 架构呢？</strong> 举个例子：若系统出现“分区”，系统中的某个节点在进行写操作。为了保证 C， 必须要禁止其他节点的读写操作，这就和 A 发生冲突了。如果为了保证 A，其他节点的读写操作正常的话，那就和 C 发生冲突了。</p><p>另外，需要补充说明的一点是： <strong>如果网络分区正常的话（系统在绝大部分时候所处的状态），也就说不需要保证 P 的时候，C 和 A 能够同时保证。</strong></p><h3 id="_3、cap-实际应用案例" tabindex="-1"><a class="header-anchor" href="#_3、cap-实际应用案例" aria-hidden="true">#</a> 3、CAP 实际应用案例</h3><p>注册中心来探讨一下 CAP 的实际应用。考虑到很多小伙伴不知道注册中心是干嘛的，这里简单以 Dubbo 为例说一说。</p><p><strong>注册中心 Registry 在其中扮演了什么角色呢？提供了什么服务呢？</strong></p><ul><li>注册中心负责服务地址的注册与查找，相当于目录服务，服务提供者和消费者只在启动时与注册中心交互，注册中心不转发请求，压力较小。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200902070.png" alt="image-20220620090251013" loading="lazy"></p><p>常见的可以作为注册中心的组件有：ZooKeeper、Eureka、Nacos...。</p><ol><li><strong>ZooKeeper 保证的是 CP。</strong> 任何时刻对 ZooKeeper 的读请求都能得到一致性的结果，但是， ZooKeeper 不保证每次请求的可用性比如在 Leader 选举过程中或者半数以上的机器不可用的时候服务就是不可用的。</li><li><strong>Eureka 保证的则是 AP。</strong> Eureka 在设计的时候就是优先保证 A （可用性）。在 Eureka 中不存在什么 Leader 节点，每个节点都是一样的、平等的。因此 Eureka 不会像 ZooKeeper 那样出现选举过程中或者半数以上的机器不可用的时候服务就是不可用的情况。 Eureka 保证即使大部分节点挂掉也不会影响正常提供服务，只要有一个节点是可用的就行了。只不过这个节点上的数据可能并不是最新的。</li><li><strong>Nacos 不仅支持 CP 也支持 AP。</strong></li></ol><h3 id="总结" tabindex="-1"><a class="header-anchor" href="#总结" aria-hidden="true">#</a> 总结</h3><p>在进行分布式系统设计和开发时，我们不应该仅仅局限在 CAP 问题上，还要关注系统的扩展性、可用性等等</p><p>在系统发生“分区”的情况下，CAP 理论只能满足 CP 或者 AP。要注意的是，这里的前提是系统发生了“分区”</p><p>如果系统没有发生“分区”的话，节点间的网络连接通信正常的话，也就不存在 P 了。这个时候，我们就可以同时保证 C 和 A 了。</p><p>总结：<strong>如果系统发生“分区”，我们要考虑选择 CP 还是 AP。如果系统没有发生“分区”的话，我们要思考如何保证 CA 。</strong></p><h2 id="_2、base理论-ap补充延申" tabindex="-1"><a class="header-anchor" href="#_2、base理论-ap补充延申" aria-hidden="true">#</a> 2、Base理论（AP补充延申）</h2><p><strong>BASE</strong> 是 <strong>Basically Available（基本可用）</strong> 、<strong>Soft-state（软状态）</strong> 和 <strong>Eventually Consistent（最终一致性）</strong> 三个短语的缩写。BASE 理论是对 CAP 中一致性 C 和可用性 A 权衡的结果，其来源于对大规模互联网系统分布式实践的总结，是基于 CAP 定理逐步演化而来的，它大大降低了我们对系统的要求。</p><h3 id="_1、base-理论的核心思想" tabindex="-1"><a class="header-anchor" href="#_1、base-理论的核心思想" aria-hidden="true">#</a> 1、BASE 理论的核心思想</h3><p>即使无法做到强一致性，但每个应用都可以根据自身业务特点，采用适当的方式来使系统达到最终一致性。</p><ul><li>也就是牺牲数据的一致性来满足系统的高可用性，系统中一部分数据不可用或者不一致时，仍需要保持系统整体“主要可用”。</li></ul><p><strong>BASE 理论本质上是对 CAP 的延伸和补充，更具体地说，是对 CAP 中 AP 方案的一个补充。</strong></p><blockquote><p>如果系统没有发生“分区”的话，节点间的网络连接通信正常的话，也就不存在 P 了。这个时候，我们就可以同时保证 C 和 A 了。因此，<strong>如果系统发生“分区”，我们要考虑选择 CP 还是 AP。如果系统没有发生“分区”的话，我们要思考如何保证 CA 。</strong></p></blockquote><p>因此，AP 方案只是在系统发生分区的时候放弃一致性，<strong>而不是永远放弃一致性</strong>。在分区故障恢复后，系统应该达到最终一致性。这一点其实就是 BASE 理论延伸的地方。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200906910.png" alt="image-20220620090604860" loading="lazy"></p><p>1、基本可用</p><p>基本可用是指分布式系统在出现不可预知故障的时候，允许损失部分可用性。但是，这绝不等价于系统不可用。</p><p><strong>什么叫允许损失部分可用性呢？</strong></p><ul><li><strong>响应时间上的损失</strong>: 正常情况下，处理用户请求需要 0.5s 返回结果，但是由于系统出现故障，处理用户请求的时间变为 3 s。</li><li><strong>系统功能上的损失</strong>：正常情况下，用户可以使用系统的全部功能，但是由于系统访问量突然剧增，系统的部分非核心功能无法使用。</li></ul><p>2、软状态</p><p>软状态指允许系统中的数据存在中间状态（<strong>CAP 理论中的数据不一致</strong>），并认为该中间状态的存在不会影响系统的整体可用性，即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。</p><p>3、最终一致性</p><p>最终一致性强调的是系统中所有的数据副本，在经过一段时间的同步后，最终能够达到一个一致的状态。因此，最终一致性的本质是需要系统保证最终数据能够达到一致，而不需要实时保证系统数据的强一致性。</p><p>分布式一致性的 3 种级别：</p><ol><li><strong>强一致性</strong> ：系统写入了什么，读出来的就是什么。</li><li><strong>弱一致性</strong> ：不一定可以读取到最新写入的值，也不保证多少时间之后读取到的数据是最新的，只是会尽量保证某个时刻达到数据一致的状态。</li><li><strong>最终一致性</strong> ：弱一致性的升级版，系统会保证在一定时间内达到数据一致的状态。</li></ol><p><strong>业界比较推崇是最终一致性级别，但是某些对数据一致要求十分严格的场景比如银行转账还是要保证强一致性。</strong></p><p>那实现最终一致性的具体方式是什么呢?</p><ul><li><strong>读时修复</strong> : 在读取数据时，检测数据的不一致，进行修复。比如 Cassandra 的 Read Repair 实现，具体来说，在向 Cassandra 系统查询数据的时候，如果检测到不同节点 的副本数据不一致，系统就自动修复数据。</li><li><strong>写时修复</strong> : 在写入数据，检测数据的不一致时，进行修复。比如 Cassandra 的 Hinted Handoff 实现。具体来说，Cassandra 集群的节点之间远程写数据的时候，如果写失败 就将数据缓存下来，然后定时重传，修复数据的不一致性。</li><li><strong>异步修复</strong> : 这个是最常用的方式，通过定时对账检测副本数据的一致性，并修复。</li></ul><p>比较推荐 <strong>写时修复</strong>，这种方式对性能消耗比较低。</p><h3 id="总结-1" tabindex="-1"><a class="header-anchor" href="#总结-1" aria-hidden="true">#</a> 总结</h3><p><strong>ACID 是数据库事务完整性的理论，CAP 是分布式系统设计理论，BASE 是 CAP 理论中 AP 方案的延伸。</strong></p><h2 id="_3、paxos-算法-分布式系统共识" tabindex="-1"><a class="header-anchor" href="#_3、paxos-算法-分布式系统共识" aria-hidden="true">#</a> 3、Paxos 算法（分布式系统共识）</h2><h3 id="_1、认识paxos算法" tabindex="-1"><a class="header-anchor" href="#_1、认识paxos算法" aria-hidden="true">#</a> 1、认识Paxos算法</h3><p>Paxos 算法是 Leslie Lamport（<a href="https://zh.wikipedia.org/wiki/%E8%8E%B1%E6%96%AF%E5%88%A9%C2%B7%E5%85%B0%E4%BC%AF%E7%89%B9" target="_blank" rel="noopener noreferrer">莱斯利·兰伯特open in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a>）在 <strong>1990</strong> 年提出了一种分布式系统 <strong>共识</strong> 算法。这也是第一个被证明完备的共识算法（前提是不存在拜占庭将军问题，也就是没有恶意节点）。</p><p>兰伯特当时提出的 Paxos 算法主要包含 2 个部分:</p><ul><li><strong>Basic Paxos 算法</strong> ： 描述的是<strong>多节点之间如何就某个值(提案 Value)达成共识</strong>。</li><li><strong>Multi-Paxos 思想</strong> ： 描述的是<strong>执行多个 Basic Paxos 实例，就一系列值达成共识</strong>。Multi-Paxos 说白了就是执行多次 Basic Paxos ，核心还是 Basic Paxos 。</li></ul><p>由于 Paxos 算法在国际上被公认的非常难以理解和实现，因此不断有人尝试简化这一算法。到了2013 年才诞生了一个比 Paxos 算法更易理解和实现的共识算法—<a href="https://javaguide.cn/distributed-system/theorem&amp;algorithm&amp;protocol/raft-algorithm.html" target="_blank" rel="noopener noreferrer">Raft 算法open in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 。更具体点来说，Raft 是Multi-Paxos的一个变种，其简化了 Multi-Paxos 的思想，变得更容易被理解以及工程实现。</p><p>针对没有恶意节点的情况，除了 Raft 算法之外，当前最常用的一些共识算法比如 <code>ZAB 协议</code>、 <code>Fast Paxos 算法</code>都是基于 Paxos 算法改进的。</p><ul><li>针对存在恶意节点的情况，一般使用的是工作量证明（POW，Proof-of-Work）、权益证明（PoS，Proof-of-Stake ）等共识算法。这类共识算法最典型的应用就是区块链，就比如说前段时间以太坊官方宣布其共识机制正在从工作量证明(PoW)转变为权益证明(PoS)。</li></ul><p>区块链系统使用的共识算法需要解决的核心问题是 <strong>拜占庭将军问题</strong> ，这和我们日常接触到的 ZooKeeper、Etcd、Consul 等分布式中间件不太一样。</p><p>Paxos 算法的定义做一个总结：</p><ul><li>Paxos 算法是兰伯特在 <strong>1990</strong> 年提出了一种分布式系统共识算法。</li><li>兰伯特当时提出的 Paxos 算法主要包含 2 个部分:Basic Paxos 算法和Multi-Paxos 思想。</li><li>Raft 算法、ZAB 协议、 Fast Paxos 算法都是基于 Paxos 算法改进而来。</li></ul><p>注意：<strong>Paxos 不是一致性算法而是共识算法，一致性和共识并不是一个概念。</strong></p><h3 id="_2、basic-paxos-算法" tabindex="-1"><a class="header-anchor" href="#_2、basic-paxos-算法" aria-hidden="true">#</a> 2、Basic Paxos 算法</h3><p>Basic Paxos 中存在 3 个重要的角色：</p><ol><li><strong>提议者（Proposer）</strong>：也可以叫做协调者（coordinator），提议者负责接受客户端发起的提议，然后尝试让接受者接受该提议，同时保证即使多个提议者的提议之间产生了冲突，那么算法都能进行下去；</li><li><strong>接受者（Acceptor）</strong>：也可以叫做投票员（voter），负责对提议者的提议投票，同时需要记住自己的投票历史；</li><li><strong>学习者（Learner）</strong>：如果有超过半数接受者就某个提议达成了共识，那么学习者就需要接受这个提议，并就该提议作出运算，然后将运算结果返回给客户端。</li></ol><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200916432.png" alt="image-20220620091610376" loading="lazy"></p><h3 id="_3、multi-paxos-思想" tabindex="-1"><a class="header-anchor" href="#_3、multi-paxos-思想" aria-hidden="true">#</a> 3、Multi Paxos 思想</h3><p>因为兰伯特提到的 Multi-Paxos 思想，缺少代码实现的必要细节(比如怎么选举领导者)，所以在理解上比较难。</p><p><strong>注意</strong> ： Multi-Paxos 只是一种思想，这种思想的核心就是通过多个 Basic Paxos 实例就一系列值达成共识。</p><p>二阶段提交是达成共识常用的方式，Basic Paxos 就是通过二阶段提交的方式来达成共识。Basic Paxos 还支持容错，少于一般的节点出现故障时，集群也能正常工作。</p><h2 id="_4、raft-算法-分布式共识" tabindex="-1"><a class="header-anchor" href="#_4、raft-算法-分布式共识" aria-hidden="true">#</a> 4、Raft 算法（分布式共识）</h2><h3 id="背景" tabindex="-1"><a class="header-anchor" href="#背景" aria-hidden="true">#</a> 背景</h3><p>当今的数据中心和应用程序在高度动态的环境中运行，为了应对高度动态的环境，它们通过额外的服务器进行横向扩展，并且根据需求进行扩展和收缩。同时，服务器和网络故障也很常见。</p><p>因此，系统必须在正常操作期间处理服务器的上下线。它们必须对变故做出反应并在几秒钟内自动适应；对客户来说的话，明显的中断通常是不可接受的。</p><p>幸运的是，分布式共识可以帮助应对这些挑战。</p><h3 id="_1、拜占庭将军" tabindex="-1"><a class="header-anchor" href="#_1、拜占庭将军" aria-hidden="true">#</a> 1、拜占庭将军</h3><blockquote><p>假设多位拜占庭将军中没有叛军，信使的信息可靠但有可能被暗杀的情况下，将军们如何达成是否要进攻的一致性决定？</p></blockquote><p>解决方案大致可以理解成：先在所有的将军中选出一个大将军，用来做出所有的决定。</p><p>举例如下：假如现在一共有 3 个将军 A，B 和 C，每个将军都有一个随机时间的倒计时器，倒计时一结束，这个将军就把自己当成大将军候选人，然后派信使传递选举投票的信息给将军 B 和 C，如果将军 B 和 C 还没有把自己当作候选人（自己的倒计时还没有结束），并且没有把选举票投给其他人，它们就会把票投给将军 A，信使回到将军 A 时，将军 A 知道自己收到了足够的票数，成为大将军。在有了大将军之后，是否需要进攻就由大将军 A 决定，然后再去派信使通知另外两个将军，自己已经成为了大将军。如果一段时间还没收到将军 B 和 C 的回复（信使可能会被暗杀），那就再重派一个信使，直到收到回复。</p><h3 id="_2、共识算法" tabindex="-1"><a class="header-anchor" href="#_2、共识算法" aria-hidden="true">#</a> 2、共识算法</h3><p>共识是可容错系统中的一个基本问题：即使面对故障，服务器也可以在共享状态上达成一致。</p><p>共识算法允许一组节点像一个整体一样一起工作，即使其中的一些节点出现故障也能够继续工作下去，其正确性主要是源于复制状态机的性质：一组<code>Server</code>的状态机计算相同状态的副本，即使有一部分的<code>Server</code>宕机了它们仍然能够继续运行。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200929097.png" alt="image-20220620092951027" loading="lazy"></p><ul><li>图-1 复制状态机架构</li></ul><p>一般通过使用<strong>复制日志</strong>来实现复制状态机。每个<code>Server</code>存储着一份包括命令序列的日志文件，状态机会按顺序执行这些命令。因为每个日志包含相同的命令，并且顺序也相同，所以每个状态机处理相同的命令序列。由于状态机是确定性的，所以处理相同的状态，得到相同的输出。</p><p>因此共识算法的工作就是保持复制日志的一致性。服务器上的共识模块从客户端接收命令并将它们添加到日志中。它与其他服务器上的共识模块通信，以确保即使某些服务器发生故障。每个日志最终包含相同顺序的请求。一旦命令被正确地复制，它们就被称为已提交。每个服务器的状态机按照日志顺序处理已提交的命令，并将输出返回给客户端，因此，这些服务器形成了一个单一的、高度可靠的状态机。</p><p><strong>适用于实际系统的共识算法通常具有以下特性</strong>：</p><ul><li>安全。确保在非拜占庭条件（也就是上文中提到的简易版拜占庭）下的安全性，包括网络延迟、分区、包丢失、复制和重新排序。</li><li>高可用。只要大多数服务器都是可操作的，并且可以相互通信，也可以与客户端进行通信，那么这些服务器就可以看作完全功能可用的。因此，一个典型的由五台服务器组成的集群可以容忍任何两台服务器端故障。假设服务器因停止而发生故障；它们稍后可能会从稳定存储上的状态中恢复并重新加入集群。</li><li>一致性不依赖时序。错误的时钟和极端的消息延迟，在最坏的情况下也只会造成可用性问题，而不会产生一致性问题。</li><li>在集群中大多数服务器响应，命令就可以完成，不会被少数运行缓慢的服务器来影响整体系统性能。</li></ul><h3 id="_3、基础" tabindex="-1"><a class="header-anchor" href="#_3、基础" aria-hidden="true">#</a> 3、基础</h3><h4 id="_1、节点类型" tabindex="-1"><a class="header-anchor" href="#_1、节点类型" aria-hidden="true">#</a> 1、节点类型</h4><p>一个 Raft 集群包括若干服务器，以典型的 5 服务器集群举例。在任意的时间，每个服务器一定会处于以下三个状态中的一个：</p><ul><li><code>Leader</code>：负责发起心跳，响应客户端，创建日志，同步日志。</li><li><code>Candidate</code>：Leader 选举过程中的临时角色，由 Follower 转化而来，发起投票参与竞选。</li><li><code>Follower</code>：接受 Leader 的心跳和日志同步数据，投票给 Candidate。</li></ul><p>在正常的情况下，只有一个服务器是 Leader，剩下的服务器是 Follower。Follower 是被动的，它们不会发送任何请求，只是响应来自 Leader 和 Candidate 的请求。</p><p><strong>服务器的状态</strong>：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200931576.png" alt="image-20220620093145520" loading="lazy"></p><h4 id="_2、任期" tabindex="-1"><a class="header-anchor" href="#_2、任期" aria-hidden="true">#</a> 2、任期</h4><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200932993.png" alt="image-20220620093233955" loading="lazy"></p><p>raft 算法将时间划分为任意长度的任期（term），任期用连续的数字表示，看作当前 term 号。<strong>每一个任期的开始都是一次选举</strong>，在选举开始时，一个或多个 Candidate 会尝试成为 Leader。如果一个 Candidate 赢得了选举，它就会在该任期内担任 Leader。如果没有选出 Leader，将会开启另一个任期，并立刻开始下一次选举。raft 算法保证在给定的一个任期最少要有一个 Leader。</p><p>每个节点都会存储当前的 term 号，当服务器之间进行通信时会交换当前的 term 号；如果有服务器发现自己的 term 号比其他人小，那么他会更新到较大的 term 值。如果一个 Candidate 或者 Leader 发现自己的 term 过期了，他会立即退回成 Follower。如果一台服务器收到的请求的 term 号是过期的，那么它会拒绝此次请求。</p><h4 id="_3、日志" tabindex="-1"><a class="header-anchor" href="#_3、日志" aria-hidden="true">#</a> 3、日志</h4><ul><li><code>entry</code>：每一个事件成为 entry，只有 Leader 可以创建 entry。entry 的内容为<code>&lt;term,index,cmd&gt;</code>其中 cmd 是可以应用到状态机的操作。</li><li><code>log</code>：由 entry 构成的数组，每一个 entry 都有一个表明自己在 log 中的 index。只有 Leader 才可以改变其他节点的 log。entry 总是先被 Leader 添加到自己的 log 数组中，然后再发起共识请求，获得同意后才会被 Leader 提交给状态机。Follower 只能从 Leader 获取新日志和当前的 commitIndex，然后把对应的 entry 应用到自己的状态机中。</li></ul><h3 id="_4、领导人选举" tabindex="-1"><a class="header-anchor" href="#_4、领导人选举" aria-hidden="true">#</a> 4、领导人选举</h3><p>raft 使用心跳机制来触发 Leader 的选举。</p><p>如果一台服务器能够收到来自 Leader 或者 Candidate 的有效信息，那么它会一直保持为 Follower 状态，并且刷新自己的 electionElapsed，重新计时。</p><p>Leader 会向所有的 Follower 周期性发送心跳来保证自己的 Leader 地位。如果一个 Follower 在一个周期内没有收到心跳信息，就叫做选举超时，然后它就会认为此时没有可用的 Leader，并且开始进行一次选举以选出一个新的 Leader。</p><p>为了开始新的选举，Follower 会自增自己的 term 号并且转换状态为 Candidate。然后他会向所有节点发起 RequestVoteRPC 请求， Candidate 的状态会持续到以下情况发生：</p><ul><li>赢得选举</li><li>其他节点赢得选举</li><li>一轮选举结束，无人胜出</li></ul><p>赢得选举的条件是：一个 Candidate 在一个任期内收到了来自集群内的多数选票<code>（N/2+1）</code>，就可以成为 Leader。</p><p>在 Candidate 等待选票的时候，它可能收到其他节点声明自己是 Leader 的心跳，此时有两种情况：</p><ul><li>该 Leader 的 term 号大于等于自己的 term 号，说明对方已经成为 Leader，则自己回退为 Follower。</li><li>该 Leader 的 term 号小于自己的 term 号，那么会拒绝该请求并让该节点更新 term。</li></ul><p>由于可能同一时刻出现多个 Candidate，导致没有 Candidate 获得大多数选票，如果没有其他手段来重新分配选票的话，那么可能会无限重复下去。</p><p>raft 使用了随机的选举超时时间来避免上述情况。每一个 Candidate 在发起选举后，都会随机化一个新的枚举超时时间，这种机制使得各个服务器能够分散开来，在大多数情况下只有一个服务器会率先超时；它会在其他服务器超时之前赢得选举。</p><h3 id="_5、日志复制" tabindex="-1"><a class="header-anchor" href="#_5、日志复制" aria-hidden="true">#</a> 5、日志复制</h3><p>一旦选出了 Leader，它就开始接受客户端的请求。每一个客户端的请求都包含一条需要被复制状态机（<code>Replicated State Mechine</code>）执行的命令。</p><p>Leader 收到客户端请求后，会生成一个 entry，包含<code>&lt;index,term,cmd&gt;</code>，再将这个 entry 添加到自己的日志末尾后，<strong>向所有的节点广播该 entry，要求其他服务器复制这条 entry</strong>。</p><p>如果 Follower 接受该 entry，则会将 entry 添加到自己的日志后面，同时返回给 Leader 同意。</p><p>如果 Leader 收到了多数的成功响应，Leader 会将这个 entry 应用到自己的状态机中，之后可以成为这个 entry 是 committed 的，并且向客户端返回执行结果。</p><p>raft 保证以下两个性质：</p><ul><li>在两个日志里，有两个 entry 拥有相同的 index 和 term，那么它们一定有相同的 cmd</li><li>在两个日志里，有两个 entry 拥有相同的 index 和 term，那么它们前面的 entry 也一定相同</li></ul><p>通过“仅有 Leader 可以生存 entry”来保证第一个性质，第二个性质需要一致性检查来进行保证。</p><p>一般情况下，Leader 和 Follower 的日志保持一致，然后，Leader 的崩溃会导致日志不一样，这样一致性检查会产生失败。Leader 通过强制 Follower 复制自己的日志来处理日志的不一致。这就意味着，在 Follower 上的冲突日志会被领导者的日志覆盖。</p><p>为了使得 Follower 的日志和自己的日志一致，Leader 需要找到 Follower 与它日志一致的地方，然后删除 Follower 在该位置之后的日志，接着把这之后的日志发送给 Follower。</p><p><code>Leader</code> 给每一个<code>Follower</code> 维护了一个 <code>nextIndex</code>，它表示 <code>Leader</code> 将要发送给该追随者的下一条日志条目的索引。当一个 <code>Leader</code> 开始掌权时，它会将 <code>nextIndex</code> 初始化为它的最新的日志条目索引数+1。如果一个 <code>Follower</code> 的日志和 <code>Leader</code> 的不一致，<code>AppendEntries</code> 一致性检查会在下一次 <code>AppendEntries RPC</code> 时返回失败。在失败之后，<code>Leader</code> 会将 <code>nextIndex</code> 递减然后重试 <code>AppendEntries RPC</code>。最终 <code>nextIndex</code> 会达到一个 <code>Leader</code> 和 <code>Follower</code> 日志一致的地方。这时，<code>AppendEntries</code> 会返回成功，<code>Follower</code> 中冲突的日志条目都被移除了，并且添加所缺少的上了 <code>Leader</code> 的日志条目。一旦 <code>AppendEntries</code> 返回成功，<code>Follower</code> 和 <code>Leader</code> 的日志就一致了，这样的状态会保持到该任期结束。</p><h3 id="_6、安全性" tabindex="-1"><a class="header-anchor" href="#_6、安全性" aria-hidden="true">#</a> 6、安全性</h3><h4 id="选举限制" tabindex="-1"><a class="header-anchor" href="#选举限制" aria-hidden="true">#</a> 选举限制</h4><p>Leader 需要保证自己存储全部已经提交的日志条目。这样才可以使日志条目只有一个流向：从 Leader 流向 Follower，Leader 永远不会覆盖已经存在的日志条目。</p><p>每个 Candidate 发送 RequestVoteRPC 时，都会带上最后一个 entry 的信息。所有节点收到投票信息时，会对该 entry 进行比较，如果发现自己的更新，则拒绝投票给该 Candidate。</p><p>判断日志新旧的方式：如果两个日志的 term 不同，term 大的更新；如果 term 相同，更长的 index 更新。</p><h4 id="节点崩溃" tabindex="-1"><a class="header-anchor" href="#节点崩溃" aria-hidden="true">#</a> 节点崩溃</h4><p>如果 Leader 崩溃，集群中的节点在 electionTimeout 时间内没有收到 Leader 的心跳信息就会触发新一轮的选主，在选主期间整个集群对外是不可用的。</p><p>如果 Follower 和 Candidate 崩溃，处理方式会简单很多。之后发送给它的 RequestVoteRPC 和 AppendEntriesRPC 会失败。由于 raft 的所有请求都是幂等的，所以失败的话会无限的重试。如果崩溃恢复后，就可以收到新的请求，然后选择追加或者拒绝 entry。</p><h4 id="时间与可用性" tabindex="-1"><a class="header-anchor" href="#时间与可用性" aria-hidden="true">#</a> 时间与可用性</h4><p>raft 的要求之一就是安全性不依赖于时间：系统不能仅仅因为一些事件发生的比预想的快一些或者慢一些就产生错误。为了保证上述要求，最好能满足以下的时间条件：</p><div class="language-text ext-text line-numbers-mode"><pre class="language-text"><code>broadcastTime &lt;&lt; electionTimeout &lt;&lt; MTBF
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><ul><li><code>broadcastTime</code>：向其他节点并发发送消息的平均响应时间；</li><li><code>electionTimeout</code>：选举超时时间；</li><li><code>MTBF(mean time between failures)</code>：单台机器的平均健康时间；</li></ul><p><code>broadcastTime</code>应该比<code>electionTimeout</code>小一个数量级，为的是使<code>Leader</code>能够持续发送心跳信息（heartbeat）来阻止<code>Follower</code>开始选举；</p><p><code>electionTimeout</code>也要比<code>MTBF</code>小几个数量级，为的是使得系统稳定运行。当<code>Leader</code>崩溃时，大约会在整个<code>electionTimeout</code>的时间内不可用；我们希望这种情况仅占全部时间的很小一部分。</p><p>由于<code>broadcastTime</code>和<code>MTBF</code>是由系统决定的属性，因此需要决定<code>electionTimeout</code>的时间。</p><p>一般来说，broadcastTime 一般为 <code>0.5～20ms</code>，electionTimeout 可以设置为 <code>10～500ms</code>，MTBF 一般为一两个月。</p><h2 id="_5、网关" tabindex="-1"><a class="header-anchor" href="#_5、网关" aria-hidden="true">#</a> 5、网关</h2><h3 id="_1、何为网关-为什么要网关" tabindex="-1"><a class="header-anchor" href="#_1、何为网关-为什么要网关" aria-hidden="true">#</a> 1、何为网关？为什么要网关？</h3><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200958411.png" alt="image-20220620095820310" loading="lazy"></p><p>微服务背景下，一个系统被拆分为多个服务，但是像安全认证，流量控制，日志，监控等功能是每个服务都需要的，没有网关的话，我们就需要在每个服务中单独实现，这使得我们做了很多重复的事情并且没有一个全局的视图来统一管理这些功能。</p><p>综上：<strong>一般情况下，网关都会提供请求转发、安全认证（身份/权限认证）、流量控制、负载均衡、容灾、日志、监控这些功能。</strong></p><p>上面介绍了这么多功能，实际上，网关主要做了一件事情：<strong>请求过滤</strong> 。</p><h3 id="_2、有哪些常见的网关系统" tabindex="-1"><a class="header-anchor" href="#_2、有哪些常见的网关系统" aria-hidden="true">#</a> 2、有哪些常见的网关系统？</h3><h4 id="netflix-zuul" tabindex="-1"><a class="header-anchor" href="#netflix-zuul" aria-hidden="true">#</a> Netflix Zuul</h4><p>Zuul 是 Netflix 开发的一款提供动态路由、监控、弹性、安全的网关服务。</p><p>Zuul 主要通过过滤器（类似于 AOP）来过滤请求，从而实现网关必备的各种功能。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206200959129.png" alt="image-20220620095927057" loading="lazy"></p><p>我们可以自定义过滤器来处理请求，并且，Zuul 生态本身就有很多现成的过滤器供我们使用。就比如限流可以直接用国外朋友写的 <a href="https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit" target="_blank" rel="noopener noreferrer">spring-cloud-zuul-ratelimitopen in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> (这里只是举例说明，一般是配合 hystrix 来做限流)：</p><div class="language-xml ext-xml line-numbers-mode"><pre class="language-xml"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>org.springframework.cloud<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>spring-cloud-starter-netflix-zuul<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>com.marcosbarbero.cloud<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>spring-cloud-zuul-ratelimit<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">&gt;</span></span>2.2.0.RELEASE<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">&gt;</span></span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Zuul 1.x 基于同步 IO，性能较差。Zuul 2.x 基于 Netty 实现了异步 IO，性能得到了大幅改进。</p><h4 id="spring-cloud-gateway" tabindex="-1"><a class="header-anchor" href="#spring-cloud-gateway" aria-hidden="true">#</a> Spring Cloud Gateway</h4><p>SpringCloud Gateway 属于 Spring Cloud 生态系统中的网关，其诞生的目标是为了替代老牌网关 **Zuul **。准确点来说，应该是 Zuul 1.x。SpringCloud Gateway 起步要比 Zuul 2.x 更早。</p><p>为了提升网关的性能，SpringCloud Gateway 基于 Spring WebFlux 。Spring WebFlux 使用 Reactor 库来实现响应式编程模型，底层基于 Netty 实现异步 IO。</p><p>Spring Cloud Gateway 的目标，不仅提供统一的路由方式，并且基于 Filter 链的方式提供了网关基本的功能，例如：安全，监控/指标，和限流。</p><p>Spring Cloud Gateway 和 Zuul 2.x 的差别不大，也是通过过滤器来处理请求。不过，<strong>目前更加推荐使用 Spring Cloud Gateway 而非 Zuul</strong>，Spring Cloud 生态对其支持更加友好。</p><h4 id="kong-基于-nginx-与-lua-的高性能-web-平台" tabindex="-1"><a class="header-anchor" href="#kong-基于-nginx-与-lua-的高性能-web-平台" aria-hidden="true">#</a> Kong（基于 Nginx 与 Lua 的高性能 Web 平台）</h4><p>Kong 是一款基于 <a href="https://github.com/openresty/" target="_blank" rel="noopener noreferrer">OpenRestyopen in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 的<strong>高性能、云原生、可扩展</strong>的网关系统。</p><p>OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台，其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。</p><p>Kong 提供了插件机制来扩展其功能。比如、在服务上启用 Zipkin 插件：</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code>$ <span class="token function">curl</span> -X POST http://kong:8001/services/<span class="token punctuation">{</span>service<span class="token punctuation">}</span>/plugins <span class="token punctuation">\</span>
    --data <span class="token string">&quot;name=zipkin&quot;</span>  <span class="token punctuation">\</span>
    --data <span class="token string">&quot;config.http_endpoint=http://your.zipkin.collector:9411/api/v2/spans&quot;</span> <span class="token punctuation">\</span>
    --data <span class="token string">&quot;config.sample_ratio=0.001&quot;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h4 id="apisix-基于go-nginx-和-kong-的替代项目" tabindex="-1"><a class="header-anchor" href="#apisix-基于go-nginx-和-kong-的替代项目" aria-hidden="true">#</a> APISIX（基于Go，NGINX 和 Kong 的替代项目）</h4><p>APISIX 是一款基于 Nginx 和 etcd 的高性能、云原生、可扩展的网关系统。</p><blockquote><p><em>etcd</em>是使用 Go 语言开发的一个开源的、高可用的分布式 key-value 存储系统，使用 Raft 协议做分布式共识。</p></blockquote><p>与传统 API 网关相比，APISIX 具有<strong>动态路由和插件热加载，特别适合微服务系统下的 API 管理</strong>。并且，APISIX 与 SkyWalking（分布式链路追踪系统）、Zipkin（分布式链路追踪系统）、Prometheus（监控系统） 等 DevOps 生态工具对接都十分方便。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201001996.png" alt="image-20220620100147916" loading="lazy"></p><p>作为 NGINX 和 Kong 的替代项目，APISIX 目前已经是 Apache 顶级开源项目，并且是最快毕业的国产开源项目。国内目前已经有很多知名企业（比如金山、有赞、爱奇艺、腾讯、贝壳）使用 APISIX 处理核心的业务流量。</p><p>根据官网介绍：“APISIX 已经生产可用，功能、性能、架构全面优于 Kong”。</p><h4 id="shenyu" tabindex="-1"><a class="header-anchor" href="#shenyu" aria-hidden="true">#</a> Shenyu</h4><p>Shenyu 是一款基于 WebFlux 的可扩展、高性能、响应式网关，Apache 顶级开源项目。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201002169.png" alt="image-20220620100251084" loading="lazy"> Shenyu 通过插件扩展功能，插件是 ShenYu 的灵魂，并且插件也是可扩展和热插拔的。不同的插件实现不同的功能。Shenyu 自带了诸如限流、熔断、转发 、重写、重定向、和路由监控等插件。</p><h2 id="_6、分布式id" tabindex="-1"><a class="header-anchor" href="#_6、分布式id" aria-hidden="true">#</a> 6、分布式ID</h2><h3 id="_1、分布式-id-介绍" tabindex="-1"><a class="header-anchor" href="#_1、分布式-id-介绍" aria-hidden="true">#</a> 1、分布式 ID 介绍</h3><blockquote><p>何为ID</p></blockquote><p>日常开发中，我们需要对系统中的各种数据使用 ID 唯一表示，比如用户 ID 对应且仅对应一个人，商品 ID 对应且仅对应一件商品，订单 ID 对应且仅对应一个订单。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201003748.png" alt="image-20220620100354687" loading="lazy"></p><p>我们现实生活中也有各种 ID，比如身份证 ID 对应且仅对应一个人、地址 ID 对应且仅对应</p><p>简单来说，<strong>ID 就是数据的唯一标识</strong>。</p><blockquote><p>何为分布式 ID？</p></blockquote><p>分布式 ID 是分布式系统下的 ID。分布式 ID 不存在与现实生活中，属于计算机系统中的一个概念。</p><p>我简单举一个分库分表的例子。</p><p>我司的一个项目，使用的是单机 MySQL 。但是，没想到的是，项目上线一个月之后，随着使用人数越来越多，整个系统的数据量将越来越大。</p><p>单机 MySQL 已经没办法支撑了，需要进行分库分表（推荐 Sharding-JDBC）。</p><p>在分库之后， 数据遍布在不同服务器上的数据库，数据库的自增主键已经没办法满足生成的主键唯一了。<strong>我们如何为不同的数据节点生成全局唯一主键呢？</strong></p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201004071.png" alt="image-20220620100438005" loading="lazy"></p><p>这个时候就需要生成<strong>分布式 ID</strong>了。</p><blockquote><p>生成分布式ID的要求</p></blockquote><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201005348.png" alt="image-20220620100502242" loading="lazy"></p><p>分布式 ID 作为分布式系统中必不可少的一环，很多地方都要用到分布式 ID。</p><p>一个最基本的分布式 ID 需要满足下面这些要求：</p><ul><li><strong>全局唯一</strong> ：ID 的全局唯一性肯定是首先要满足的！</li><li><strong>高性能</strong> ： 分布式 ID 的生成速度要快，对本地资源消耗要小。</li><li><strong>高可用</strong> ：生成分布式 ID 的服务要保证可用性无限接近于 100%。</li><li><strong>方便易用</strong> ：拿来即用，使用方便，快速接入！</li></ul><p>除了这些之外，一个比较好的分布式 ID 还应保证：</p><ul><li><strong>安全</strong> ：ID 中不包含敏感信息。</li><li><strong>有序递增</strong> ：如果要把 ID 存放在数据库的话，ID 的有序性可以提升数据库写入速度。并且，很多时候 ，我们还很有可能会直接通过 ID 来进行排序。</li><li><strong>有具体的业务含义</strong> ：生成的 ID 如果能有具体的业务含义，可以让定位问题以及开发更透明化（通过 ID 就能确定是哪个业务）。</li><li><strong>独立部署</strong> ：也就是分布式系统单独有一个发号器服务，专门用来生成分布式 ID。这样就生成 ID 的服务可以和业务相关的服务解耦。不过，这样同样带来了网络调用消耗增加的问题。总的来说，如果需要用到分布式 ID 的场景比较多的话，独立部署的发号器服务还是很有必要的。</li></ul><h3 id="_2、-分布式-id-常见解决方案" tabindex="-1"><a class="header-anchor" href="#_2、-分布式-id-常见解决方案" aria-hidden="true">#</a> 2、 分布式 ID 常见解决方案</h3><h4 id="数据库方案" tabindex="-1"><a class="header-anchor" href="#数据库方案" aria-hidden="true">#</a> 数据库方案</h4><blockquote><p>1、数据库主键自增。</p></blockquote><p>这种方式的优缺点也比较明显：</p><ul><li><strong>优点</strong> ：实现起来比较简单、ID 有序递增、存储消耗空间小</li><li><strong>缺点</strong> ： 支持的并发量不大、存在数据库单点问题（可以使用数据库集群解决，不过增加了复杂度）、ID 没有具体业务含义、安全问题（比如根据订单 ID 的递增规律就能推算出每天的订单量，商业机密啊！ ）、每次获取 ID 都要访问一次数据库（增加了对数据库的压力，获取速度也慢）</li></ul><blockquote><p>2、数据库号段模式</p></blockquote><p>方案：如果我们可以批量获取，然后存在在内存里面，需要用到的时候，直接从内存里面拿就舒服了！这也就是我们说的 <strong>基于数据库的号段模式来生成分布式 ID。</strong></p><ul><li>举例：滴滴开源的<a href="https://github.com/didi/tinyid/wiki/tinyid%E5%8E%9F%E7%90%86%E4%BB%8B%E7%BB%8D" target="_blank" rel="noopener noreferrer">Tinyidopen in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 就是基于这种方式来做的。不过，TinyId 使用了双号段缓存、增加多 db 支持等方式来进一步优化。</li></ul><p><code>current_max_id</code> 字段和<code>step</code>字段主要用于获取批量 ID，获取的批量 id 为： <code>current_max_id ~ current_max_id+step</code>。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201007203.png" alt="image-20220620100724153" loading="lazy"></p><p><code>version</code> 字段主要用于解决并发问题（乐观锁）,<code>biz_type</code> 主要用于表示业务类型。</p><p>相比于数据库主键自增的方式，<strong>数据库的号段模式对于数据库的访问次数更少，数据库压力更小。</strong></p><p>另外，为了避免单点问题，你可以从使用主从模式来提高可用性。</p><p><strong>数据库号段模式的优缺点:</strong></p><ul><li><strong>优点</strong> ：ID 有序递增、存储消耗空间小</li><li><strong>缺点</strong> ：存在数据库单点问题（可以使用数据库集群解决，不过增加了复杂度）、ID 没有具体业务含义、安全问题（比如根据订单 ID 的递增规律就能推算出每天的订单量，商业机密啊！ ）</li></ul><blockquote><p>3、NoSQL</p></blockquote><p>我们通过 Redis 的 <code>incr</code> 命令即可实现对 id 原子顺序递增。</p><p>为了提高可用性和并发，我们可以使用 Redis Cluster。Redis Cluster 是 Redis 官方提供的 Redis 集群解决方案（3.0+版本）。</p><p>除了 Redis Cluster 之外，你也可以使用开源的 Redis 集群方案<a href="https://github.com/CodisLabs/codis" target="_blank" rel="noopener noreferrer">Codisopen in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> （大规模集群比如上百个节点的时候比较推荐）。</p><p>除了高可用和并发之外，我们知道 Redis 基于内存，我们需要持久化数据，避免重启机器或者机器故障后数据丢失。Redis 支持两种不同的持久化方式：<strong>快照（snapshotting，RDB）</strong>、<strong>只追加文件（append-only file, AOF）</strong>。 并且，Redis 4.0 开始支持 <strong>RDB 和 AOF 的混合持久化</strong>（默认关闭，可以通过配置项 <code>aof-use-rdb-preamble</code> 开启）。</p><p><strong>Redis 方案的优缺点：</strong></p><ul><li><strong>优点</strong> ： 性能不错并且生成的 ID 是有序递增的</li><li><strong>缺点</strong> ： 和数据库主键自增方案的缺点类似</li></ul><h4 id="算法" tabindex="-1"><a class="header-anchor" href="#算法" aria-hidden="true">#</a> 算法</h4><blockquote><p>UUID</p></blockquote><p>UUID 是 Universally Unique Identifier（通用唯一标识符） 的缩写。UUID 包含 32 个 16 进制数字（8-4-4-4-12）。</p><p>JDK 就提供了现成的生成 UUID 的方法，一行代码就行了。</p><p>比如使用 UUID 作为 MySQL 数据库主键的时候就非常不合适：</p><ul><li>数据库主键要尽量越短越好，而 UUID 的消耗的存储空间比较大（32 个字符串，128 位）。</li><li>UUID 是无顺序的，InnoDB 引擎下，数据库主键的无序性会严重影响数据库性能。</li></ul><p>最后，我们再简单分析一下 <strong>UUID 的优缺点</strong> （面试的时候可能会被问到的哦！） :</p><ul><li><strong>优点</strong> ：生成速度比较快、简单易用</li><li><strong>缺点</strong> ： 存储消耗空间大（32 个字符串，128 位） 、 不安全（基于 MAC 地址生成 UUID 的算法会造成 MAC 地址泄露)、无序（非自增）、没有具体业务含义、需要解决重复 ID 问题（当机器时间不对的情况下，可能导致会产生重复 ID）</li></ul><blockquote><p>Snowflake(雪花算法)</p></blockquote><p>Snowflake 是 Twitter 开源的分布式 ID 生成算法。Snowflake 由 64 bit 的二进制数字组成，这 64bit 的二进制被分成了几部分，每一部分存储的数据都有特定的含义：</p><ul><li><strong>第 0 位</strong>： 符号位（标识正负），始终为 0，没有用，不用管。</li><li><strong>第 1~41 位</strong> ：一共 41 位，用来表示时间戳，单位是毫秒，可以支撑 2 ^41 毫秒（约 69 年）</li><li><strong>第 42~52 位</strong> ：一共 10 位，一般来说，前 5 位表示机房 ID，后 5 位表示机器 ID（实际项目中可以根据实际情况调整）。这样就可以区分不同集群/机房的节点。</li><li><strong>第 53~64 位</strong> ：一共 12 位，用来表示序列号。 序列号为自增值，代表单台机器每毫秒能够产生的最大 ID 数(2^12 = 4096),也就是说单台机器每毫秒最多可以生成 4096 个 唯一 ID。</li></ul><p>如果你想要使用 Snowflake 算法的话，一般不需要你自己再造轮子。有很多基于 Snowflake 算法的开源实现比如美团 的 Leaf、百度的 UidGenerator，并且这些开源实现对原有的 Snowflake 算法进行了优化。</p><p>另外，在实际项目中，我们一般也会对 Snowflake 算法进行改造，最常见的就是在 Snowflake 算法生成的 ID 中加入业务类型信息。</p><p>我们再来看看 Snowflake 算法的优缺点 ：</p><ul><li><strong>优点</strong> ：生成速度比较快、生成的 ID 有序递增、比较灵活（可以对 Snowflake 算法进行简单的改造比如加入业务 ID）</li><li><strong>缺点</strong> ： 需要解决重复 ID 问题（依赖时间，当机器时间不对的情况下，可能导致会产生重复 ID）。</li></ul><h4 id="开源框架" tabindex="-1"><a class="header-anchor" href="#开源框架" aria-hidden="true">#</a> 开源框架</h4><blockquote><p>UidGenerator(百度)</p></blockquote><p><a href="https://github.com/baidu/uid-generator" target="_blank" rel="noopener noreferrer">UidGeneratoropen in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 是百度开源的一款基于 Snowflake(雪花算法)的唯一 ID 生成器。</p><p>不过，UidGenerator 对 Snowflake(雪花算法)进行了改进，生成的唯一 ID 组成如下。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201012539.png" alt="image-20220620101231475" loading="lazy"></p><p>可以看出，和原始 Snowflake(雪花算法)生成的唯一 ID 的组成不太一样。并且，上面这些参数我们都可以自定义。</p><p>自 18 年后，UidGenerator 就基本没有再维护了。</p><blockquote><p>Leaf(美团)</p></blockquote><p><strong><a href="https://github.com/Meituan-Dianping/Leaf" target="_blank" rel="noopener noreferrer">Leafopen in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></strong> 是美团开源的一个分布式 ID 解决方案 。这个项目的名字 Leaf（树叶） 起源于德国哲学家、数学家莱布尼茨的一句话： “There are no two identical leaves in the world”（世界上没有两片相同的树叶） 。这名字起得真心挺不错的，有点文艺青年那味了！</p><p>Leaf 提供了 <strong>号段模式</strong> 和 <strong>Snowflake(雪花算法)</strong> 这两种模式来生成分布式 ID。并且，它支持双号段，还解决了雪花 ID 系统时钟回拨问题。不过，时钟问题的解决需要弱依赖于 Zookeeper 。</p><p>Leaf 的诞生主要是为了解决美团各个业务线生成分布式 ID 的方法多种多样以及不可靠的问题。</p><p>Leaf 对原有的号段模式进行改进，比如它这里增加了双号段避免获取 DB 在获取号段的时候阻塞请求获取 ID 的线程。简单来说，就是我一个号段还没用完之前，我自己就主动提前去获取下一个号段</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201013406.png" alt="image-20220620101349337" loading="lazy"></p><p>根据项目 README 介绍，在 4C8G VM 基础上，通过公司 RPC 方式调用，QPS 压测结果近 5w/s，TP999 1ms。</p><blockquote><p>Tinyid(滴滴)</p></blockquote><p><a href="https://github.com/didi/tinyid" target="_blank" rel="noopener noreferrer">Tinyidopen in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 是滴滴开源的一款基于数据库号段模式的唯一 ID 生成器。</p><p>数据库号段模式的原理我们在上面已经介绍过了。<strong>Tinyid 有哪些亮点呢？</strong></p><p>为了搞清楚这个问题，我们先来看看基于数据库号段模式的简单架构方案。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201014126.png" alt="image-20220620101426036" loading="lazy"></p><p>在这种架构模式下，我们通过 HTTP 请求向发号器服务申请唯一 ID。负载均衡 router 会把我们的请求送往其中的一台 tinyid-server。</p><p>这种方案有什么问题呢？在我看来（Tinyid 官方 wiki 也有介绍到），主要由下面这 2 个问题：</p><ul><li>获取新号段的情况下，程序获取唯一 ID 的速度比较慢。</li><li>需要保证 DB 高可用，这个是比较麻烦且耗费资源的。</li></ul><p>除此之外，HTTP 调用也存在网络开销。</p><p>Tinyid 的原理比较简单，其架构如下图所示：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201014598.png" alt="image-20220620101453474" loading="lazy"></p><p>相比于基于数据库号段模式的简单架构方案，Tinyid 方案主要做了下面这些优化：</p><ul><li><strong>双号段缓存</strong> ：为了避免在获取新号段的情况下，程序获取唯一 ID 的速度比较慢。 Tinyid 中的号段在用到一定程度的时候，就会去异步加载下一个号段，保证内存中始终有可用号段。</li><li><strong>增加多 db 支持</strong> ：支持多个 DB，并且，每个 DB 都能生成唯一 ID，提高了可用性。</li><li><strong>增加 tinyid-client</strong> ：纯本地操作，无 HTTP 请求消耗，性能和可用性都有很大提升。</li></ul><p>Tinyid 的优缺点这里就不分析了，结合数据库号段模式的优缺点和 Tinyid 的原理就能知道。</p><h3 id="分布式-id-生成方案总结" tabindex="-1"><a class="header-anchor" href="#分布式-id-生成方案总结" aria-hidden="true">#</a> 分布式 ID 生成方案总结</h3><p>像 ZooKeeper 这类中间件也可以帮助我们生成唯一 ID。<strong>没有银弹，一定要结合实际项目来选择最适合自己的方案。</strong></p><hr><h2 id="_7、rpc" tabindex="-1"><a class="header-anchor" href="#_7、rpc" aria-hidden="true">#</a> 7、RPC</h2><h3 id="_1、rpc基础" tabindex="-1"><a class="header-anchor" href="#_1、rpc基础" aria-hidden="true">#</a> 1、RPC基础</h3><p><strong>RPC（Remote Procedure Call）</strong> 即远程过程调用，通过名字我们就能看出 RPC 关注的是远程调用而非本地调用。</p><p><strong>为什么要 RPC ？</strong> 因为，两个不同的服务器上的服务提供的方法不在一个内存空间，所以，需要通过网络编程才能传递方法调用所需要的参数。并且，方法调用的结果也需要通过网络编程来接收。但是，如果我们自己手动网络编程来实现这个调用过程的话工作量是非常大的，因为，我们需要考虑底层传输方式（TCP还是UDP）、序列化方式等等方面。</p><p><strong>RPC 能帮助我们做什么呢？</strong> 简单来说，通过 RPC 可以帮助我们调用远程计算机上某个服务的方法，这个过程就像调用本地方法一样简单。并且！我们不需要了解底层网络编程的具体细节。</p><p>举个例子：两个不同的服务 A、B 部署在两台不同的机器上，服务 A 如果想要调用服务 B 中的某个方法的话就可以通过 RPC 来做。</p><p>一言蔽之：<strong>RPC 的出现就是为了让你调用远程方法像调用本地方法一样简单。</strong></p><h3 id="_2、rpc-的原理是什么" tabindex="-1"><a class="header-anchor" href="#_2、rpc-的原理是什么" aria-hidden="true">#</a> 2、RPC 的原理是什么?</h3><p>为了能够帮助小伙伴们理解 RPC 原理，我们可以将整个 RPC的 核心功能看作是下面👇 6 个部分实现的：</p><ol><li><strong>客户端（服务消费端）</strong> ：调用远程方法的一端。</li><li><strong>客户端 Stub（桩）</strong> ： 这其实就是一代理类。代理类主要做的事情很简单，就是把你调用方法、类、方法参数等信息传递到服务端。</li><li><strong>网络传输</strong> ： 网络传输就是你要把你调用的方法的信息比如说参数啊这些东西传输到服务端，然后服务端执行完之后再把返回结果通过网络传输给你传输回来。网络传输的实现方式有很多种比如最近基本的 Socket或者性能以及封装更加优秀的 Netty（推荐）。</li><li><strong>服务端 Stub（桩）</strong> ：这个桩就不是代理类了。我觉得理解为桩实际不太好，大家注意一下就好。这里的服务端 Stub 实际指的就是接收到客户端执行方法的请求后，去指定对应的方法然后返回结果给客户端的类。</li><li><strong>服务端（服务提供端）</strong> ：提供远程方法的一端。</li></ol><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201200411.png" alt="image-20220620120054311" loading="lazy"></p><ol><li>服务消费端（client）以本地调用的方式调用远程服务；</li><li>客户端 Stub（client stub） 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体（序列化）：<code>RpcRequest</code>；</li><li>客户端 Stub（client stub） 找到远程服务的地址，并将消息发送到服务提供端；</li><li>服务端 Stub（桩）收到消息将消息反序列化为Java对象: <code>RpcRequest</code>；</li><li>服务端 Stub（桩）根据<code>RpcRequest</code>中的类、方法、方法参数等信息调用本地的方法；</li><li>服务端 Stub（桩）得到方法执行结果并将组装成能够进行网络传输的消息体：<code>RpcResponse</code>（序列化）发送至消费方；</li><li>客户端 Stub（client stub）接收到消息并将消息反序列化为Java对象:<code>RpcResponse</code> ，这样也就得到了最终结果。over!</li></ol><h3 id="dubbo知识点-面试题总结" tabindex="-1"><a class="header-anchor" href="#dubbo知识点-面试题总结" aria-hidden="true">#</a> Dubbo知识点&amp;面试题总结</h3><h4 id="_1、什么是-dubbo" tabindex="-1"><a class="header-anchor" href="#_1、什么是-dubbo" aria-hidden="true">#</a> 1、什么是 Dubbo?</h4><p><a href="https://github.com/apache/dubbo" target="_blank" rel="noopener noreferrer">Apache Dubboopen in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> |ˈdʌbəʊ| 是一款高性能、轻量级的开源 Java RPC 框架。</p><p>根据 <a href="https://dubbo.apache.org/zh/" target="_blank" rel="noopener noreferrer">Dubbo 官方文档open in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a>的介绍，Dubbo 提供了六大核心能力</p><ol><li>面向接口代理的高性能RPC调用。</li><li>智能容错和负载均衡。</li><li>服务自动注册和发现。</li><li>高度可扩展能力。</li><li>运行期流量调度。</li><li>可视化的服务治理与运维。</li></ol><p>简单来说就是： <strong>Dubbo 不光可以帮助我们调用远程服务，还提供了一些其他开箱即用的功能比如智能负载均衡。</strong></p><p>Dubbo 目前已经有接近 34.4 k 的 Star 。</p><p>在 <strong>2020 年度 OSC 中国开源项目</strong> 评选活动中，Dubbo 位列开发框架和基础组件类项目的第7名。相比几年前来说，热度和排名有所下降。</p><p>Dubbo 是由<strong>阿里</strong>开源，后来加入了 Apache 。正是由于 Dubbo 的出现，才使得越来越多的公司开始使用以及接受分布式架构。</p><h4 id="_2、为什么要用-dubbo" tabindex="-1"><a class="header-anchor" href="#_2、为什么要用-dubbo" aria-hidden="true">#</a> 2、为什么要用 Dubbo?</h4><p>分布式服务架构下，系统被拆分成不同的服务比如短信服务、安全服务，每个服务独立提供系统的某个核心服务。</p><p>我们可以使用 Java RMI（Java Remote Method Invocation）、Hessian这种支持远程调用的框架来简单地暴露和引用远程服务。但是！当服务越来越多之后，服务调用关系越来越复杂。当应用访问压力越来越大后，负载均衡以及服务监控的需求也迫在眉睫。我们可以用 F5 这类硬件来做负载均衡，但这样增加了成本，并且存在单点故障的风险。</p><p>不过，Dubbo 的出现让上述问题得到了解决。<strong>Dubbo 帮助我们解决了什么问题呢？</strong></p><ol><li><strong>负载均衡</strong> ： 同一个服务部署在不同的机器时该调用哪一台机器上的服务。</li><li><strong>服务调用链路生成</strong> ： 随着系统的发展，服务越来越多，服务间依赖关系变得错踪复杂，甚至分不清哪个应用要在哪个应用之前启动，架构师都不能完整的描述应用的架构关系。Dubbo 可以为我们解决服务之间互相是如何调用的。</li><li><strong>服务访问压力以及时长统计、资源调度和治理</strong> ：基于访问压力实时管理集群容量，提高集群利用率。</li><li>......</li></ol><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201202360.png" alt="image-20220620120251230" loading="lazy"></p><p>Dubbo 除了能够应用在分布式系统中，也可以应用在现在比较火的微服务系统中。不过，由于 Spring Cloud 在微服务中应用更加广泛，所以，我觉得一般我们提 Dubbo 的话，大部分是<strong>分布式系统</strong>的情况。</p><h4 id="_3、分布式基础" tabindex="-1"><a class="header-anchor" href="#_3、分布式基础" aria-hidden="true">#</a> 3、分布式基础</h4><blockquote><p>什么是分布式?</p></blockquote><p>分布式或者说 SOA 分布式重要的就是面向服务，说简单的分布式就是我们把整个系统拆分成不同的服务然后将这些服务放在不同的服务器上减轻单体服务的压力提高并发量和性能。比如电商系统可以简单地拆分成订单系统、商品系统、登录系统等等，拆分之后的每个服务可以部署在不同的机器上，如果某一个服务的访问量比较大的话也可以将这个服务同时部署在多台机器上。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201208324.png" alt="image-20220620120805264" loading="lazy"></p><blockquote><p>为什么要分布式?</p></blockquote><p>从开发角度来讲单体应用的代码都集中在一起，而分布式系统的代码根据业务被拆分。所以，每个团队可以负责一个服务的开发，这样提升了开发效率。另外，代码根据业务拆分之后更加便于维护和扩展。</p><p>另外，我觉得将系统拆分成分布式之后<strong>不光便于系统扩展和维护，更能提高整个系统的性能</strong>。你想一想嘛？把整个系统拆分成不同的服务/系统，然后每个服务/系统 单独部署在一台服务器上，是不是很大程度上提高了系统性能呢？</p><h4 id="_4、-dubbo-架构" tabindex="-1"><a class="header-anchor" href="#_4、-dubbo-架构" aria-hidden="true">#</a> 4、 Dubbo 架构</h4><h5 id="_1、dubbo-架构中的核心角色有哪些" tabindex="-1"><a class="header-anchor" href="#_1、dubbo-架构中的核心角色有哪些" aria-hidden="true">#</a> 1、Dubbo 架构中的核心角色有哪些？</h5><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201209633.png" alt="image-20220620120914552" loading="lazy"></p><p>上述节点简单介绍以及他们之间的关系：</p><ul><li><strong>Container：</strong> 服务运行容器，负责加载、运行服务提供者。必须。</li><li><strong>Provider：</strong> 暴露服务的服务提供方，会向注册中心注册自己提供的服务。必须。</li><li><strong>Consumer：</strong> 调用远程服务的服务消费方，会向注册中心订阅自己所需的服务。必须。</li><li><strong>Registry：</strong> 服务注册与发现的注册中心。注册中心会返回服务提供者地址列表给消费者。非必须。</li><li><strong>Monitor：</strong> 统计服务的调用次数和调用时间的监控中心。服务消费者和提供者会定时发送统计数据到监控中心。 非必须。</li></ul><h5 id="_2、dubbo-中的-invoker-概念了解么" tabindex="-1"><a class="header-anchor" href="#_2、dubbo-中的-invoker-概念了解么" aria-hidden="true">#</a> 2、Dubbo 中的 Invoker 概念了解么？</h5><p><code>Invoker</code> 是 Dubbo 领域模型中非常重要的一个概念，你如果阅读过 Dubbo 源码的话，你会无数次看到这玩意。就比如下面我要说的负载均衡这块的源码中就有大量 <code>Invoker</code> 的身影。</p><p>简单来说，<code>Invoker</code> 就是 Dubbo 对远程调用的抽象。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201209290.png" alt="image-20220620120959178" loading="lazy"></p><p>按照 Dubbo 官方的话来说，<code>Invoker</code> 分为</p><ul><li>服务提供 <code>Invoker</code></li><li>服务消费 <code>Invoker</code></li></ul><p>假如我们需要调用一个远程方法，我们需要动态代理来屏蔽远程调用的细节吧！我们屏蔽掉的这些细节就依赖对应的 <code>Invoker</code> 实现， <code>Invoker</code> 实现了真正的远程服务调用。</p><h5 id="_3、dubbo-的工作原理了解么" tabindex="-1"><a class="header-anchor" href="#_3、dubbo-的工作原理了解么" aria-hidden="true">#</a> 3、Dubbo 的工作原理了解么？</h5><p>左边淡蓝背景的为服务消费方使用的接口，右边淡绿色背景的为服务提供方使用的接口，位于中轴线上的为双方都用到的接口。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201210314.png" alt="image-20220620121031103" loading="lazy"></p><ul><li><strong>config 配置层</strong>：Dubbo相关的配置。支持代码配置，同时也支持基于 Spring 来做配置，以 <code>ServiceConfig</code>, <code>ReferenceConfig</code> 为中心</li><li><strong>proxy 服务代理层</strong>：调用远程方法像调用本地的方法一样简单的一个关键，真实调用过程依赖代理类，以 <code>ServiceProxy</code> 为中心。</li><li><strong>registry 注册中心层</strong>：封装服务地址的注册与发现。</li><li><strong>cluster 路由层</strong>：封装多个提供者的路由及负载均衡，并桥接注册中心，以 <code>Invoker</code> 为中心。</li><li><strong>monitor 监控层</strong>：RPC 调用次数和调用时间监控，以 <code>Statistics</code> 为中心。</li><li><strong>protocol 远程调用层</strong>：封装 RPC 调用，以 <code>Invocation</code>, <code>Result</code> 为中心。</li><li><strong>exchange 信息交换层</strong>：封装请求响应模式，同步转异步，以 <code>Request</code>, <code>Response</code> 为中心。</li><li><strong>transport 网络传输层</strong>：抽象 mina 和 netty 为统一接口，以 <code>Message</code> 为中心。</li><li><strong>serialize 数据序列化层</strong> ：对需要在网络传输的数据进行序列化。</li></ul><h5 id="_4、dubbo-的-spi-机制了解么-如何扩展-dubbo-中的默认实现" tabindex="-1"><a class="header-anchor" href="#_4、dubbo-的-spi-机制了解么-如何扩展-dubbo-中的默认实现" aria-hidden="true">#</a> 4、Dubbo 的 SPI 机制了解么？ 如何扩展 Dubbo 中的默认实现？</h5><p>SPI（Service Provider Interface） 机制被大量用在开源项目中，它可以帮助我们动态寻找服务/功能（比如负载均衡策略）的实现。</p><p>SPI 的具体原理是这样的：我们将接口的实现类放在配置文件中，我们在程序运行过程中读取配置文件，通过反射加载实现类。这样，我们可以在运行的时候，动态替换接口的实现类。和 IoC 的解耦思想是类似的。</p><p>Java 本身就提供了 SPI 机制的实现。不过，Dubbo 没有直接用，而是对 Java原生的 SPI机制进行了增强，以便更好满足自己的需求。</p><p><strong>那我们如何扩展 Dubbo 中的默认实现呢？</strong></p><p>比如说我们想要实现自己的负载均衡策略，我们创建对应的实现类 <code>XxxLoadBalance</code> 实现 <code>LoadBalance</code> 接口或者 <code>AbstractLoadBalance</code> 类。</p><div class="language-java ext-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>xxx</span><span class="token punctuation">;</span>
 
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>dubbo<span class="token punctuation">.</span>rpc<span class="token punctuation">.</span>cluster<span class="token punctuation">.</span></span><span class="token class-name">LoadBalance</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>dubbo<span class="token punctuation">.</span>rpc<span class="token punctuation">.</span></span><span class="token class-name">Invoker</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>dubbo<span class="token punctuation">.</span>rpc<span class="token punctuation">.</span></span><span class="token class-name">Invocation</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>dubbo<span class="token punctuation">.</span>rpc<span class="token punctuation">.</span></span><span class="token class-name">RpcException</span></span><span class="token punctuation">;</span> 
 
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">XxxLoadBalance</span> <span class="token keyword">implements</span> <span class="token class-name">LoadBalance</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span></span> <span class="token class-name">Invoker</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span></span> <span class="token function">select</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Invoker</span><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span><span class="token punctuation">&gt;</span></span> invokers<span class="token punctuation">,</span> <span class="token class-name">Invocation</span> invocation<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">RpcException</span> <span class="token punctuation">{</span>
        <span class="token comment">// ...</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>我们将这个实现类的路径写入到<code>resources</code> 目录下的 <code>META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance</code>文件中即可。</p><div class="language-text ext-text line-numbers-mode"><pre class="language-text"><code>src
 |-main
    |-java
        |-com
            |-xxx
                |-XxxLoadBalance.java (实现LoadBalance接口)
    |-resources
        |-META-INF
            |-dubbo
                |-org.apache.dubbo.rpc.cluster.LoadBalance (纯文本文件，内容为：xxx=com.xxx.XxxLoadBalance)
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>org.apache.dubbo.rpc.cluster.LoadBalance：</p><div class="language-text ext-text line-numbers-mode"><pre class="language-text"><code>xxx=com.xxx.XxxLoadBalance
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><h5 id="_5、dubbo-的微内核架构了解吗" tabindex="-1"><a class="header-anchor" href="#_5、dubbo-的微内核架构了解吗" aria-hidden="true">#</a> 5、Dubbo 的微内核架构了解吗？</h5><p>Dubbo 采用 微内核（Microkernel） + 插件（Plugin） 模式，简单来说就是微内核架构。微内核只负责组装插件。</p><p><strong>何为微内核架构呢？</strong> 《软件架构模式》 这本书是这样介绍的：</p><blockquote><p>微内核架构模式（有时被称为插件架构模式）是实现基于产品应用程序的一种自然模式。基于产品的应用程序是已经打包好并且拥有不同版本，可作为第三方插件下载的。然后，很多公司也在开发、发布自己内部商业应用像有版本号、说明及可加载插件式的应用软件（这也是这种模式的特征）。微内核系统可让用户添加额外的应用如插件，到核心应用，继而提供了可扩展性和功能分离的用法。</p></blockquote><p>微内核架构包含两类组件：<strong>核心系统（core system）</strong> 和 <strong>插件模块（plug-in modules）</strong>。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201213398.png" alt="image-20220620121338333" loading="lazy"></p><p>核心系统提供系统所需核心能力，插件模块可以扩展系统的功能。因此， 基于微内核架构的系统，非常易于扩展功能。</p><p>我们常见的一些IDE，都可以看作是基于微内核架构设计的。绝大多数 IDE比如IDEA、VSCode都提供了插件来丰富自己的功能。</p><p>正是因为Dubbo基于微内核架构，才使得我们可以随心所欲替换Dubbo的功能点。比如你觉得Dubbo 的序列化模块实现的不满足自己要求，没关系啊！你自己实现一个序列化模块就好了啊！</p><p>通常情况下，微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期。Dubbo 不想依赖 Spring 等 IoC 容器，也不想自己造一个小的 IoC 容器（过度设计），因此采用了一种最简单的 Factory 方式管理插件 ：<strong>JDK 标准的 SPI 扩展机制</strong> （<code>java.util.ServiceLoader</code>）。</p><h5 id="_6、关于dubbo架构的一些自测小问题" tabindex="-1"><a class="header-anchor" href="#_6、关于dubbo架构的一些自测小问题" aria-hidden="true">#</a> 6、关于Dubbo架构的一些自测小问题</h5><p>注册中心的作用了解么？</p><ul><li>注册中心负责服务地址的注册与查找，相当于目录服务，服务提供者和消费者只在启动时与注册中心交互。</li></ul><p>服务提供者宕机后，注册中心会做什么？</p><ul><li>注册中心会立即推送事件通知消费者。</li></ul><p>监控中心的作用呢？</p><ul><li>监控中心负责统计各服务调用次数，调用时间等。</li></ul><p>注册中心和监控中心都宕机的话，服务都会挂掉吗？</p><ul><li>不会。两者都宕机也不影响已运行的提供者和消费者，消费者在本地缓存了提供者列表。注册中心和监控中心都是可选的，服务消费者可以直连服务提供者。</li></ul><h5 id="_7、dubbo-的负载均衡策略" tabindex="-1"><a class="header-anchor" href="#_7、dubbo-的负载均衡策略" aria-hidden="true">#</a> 7、Dubbo 的负载均衡策略</h5><h6 id="什么是负载均衡" tabindex="-1"><a class="header-anchor" href="#什么是负载均衡" aria-hidden="true">#</a> 什么是负载均衡？</h6><p>负载均衡改善了跨多个计算资源（例如计算机，计算机集群，网络链接，中央处理单元或磁盘驱动）的工作负载分布。负载平衡旨在优化资源使用，最大化吞吐量，最小化响应时间，并避免任何单个资源的过载。使用具有负载平衡而不是单个组件的多个组件可以通过冗余提高可靠性和可用性。负载平衡通常涉及专用软件或硬件。</p><ul><li>我们的系统中的某个服务的访问量特别大，我们将这个服务部署在了多台服务器上，当客户端发起请求的时候，多台服务器都可以处理这个请求。那么，如何正确选择处理该请求的服务器就很关键。假如，你就要一台服务器来处理该服务的请求，那该服务部署在多台服务器的意义就不复存在了。负载均衡就是为了避免单个服务器响应同一请求，容易造成服务器宕机、崩溃等问题，我们从负载均衡的这四个字就能明显感受到它的意义。</li></ul><h6 id="dubbo-提供的负载均衡策略有哪些" tabindex="-1"><a class="header-anchor" href="#dubbo-提供的负载均衡策略有哪些" aria-hidden="true">#</a> Dubbo 提供的负载均衡策略有哪些？</h6><p>在集群负载均衡时，Dubbo 提供了多种均衡策略，默认为 <code>random</code> 随机调用。我们还可以自行扩展负载均衡策略（参考Dubbo SPI机制）。</p><p>在 Dubbo 中，所有负载均衡实现类均继承自 <code>AbstractLoadBalance</code>，该类实现了 <code>LoadBalance</code> 接口，并封装了一些公共的逻辑。</p><div class="language-java ext-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">AbstractLoadBalance</span> <span class="token keyword">implements</span> <span class="token class-name">LoadBalance</span> <span class="token punctuation">{</span>

    <span class="token keyword">static</span> <span class="token keyword">int</span> <span class="token function">calculateWarmupWeight</span><span class="token punctuation">(</span><span class="token keyword">int</span> uptime<span class="token punctuation">,</span> <span class="token keyword">int</span> warmup<span class="token punctuation">,</span> <span class="token keyword">int</span> weight<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span></span> <span class="token class-name">Invoker</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span></span> <span class="token function">select</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Invoker</span><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span><span class="token punctuation">&gt;</span></span> invokers<span class="token punctuation">,</span> <span class="token class-name">URL</span> url<span class="token punctuation">,</span> <span class="token class-name">Invocation</span> invocation<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">protected</span> <span class="token keyword">abstract</span> <span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span></span> <span class="token class-name">Invoker</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span></span> <span class="token function">doSelect</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Invoker</span><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">&gt;</span><span class="token punctuation">&gt;</span></span> invokers<span class="token punctuation">,</span> <span class="token class-name">URL</span> url<span class="token punctuation">,</span> <span class="token class-name">Invocation</span> invocation<span class="token punctuation">)</span><span class="token punctuation">;</span>


    <span class="token keyword">int</span> <span class="token function">getWeight</span><span class="token punctuation">(</span><span class="token class-name">Invoker</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">&gt;</span></span> invoker<span class="token punctuation">,</span> <span class="token class-name">Invocation</span> invocation<span class="token punctuation">)</span> <span class="token punctuation">{</span>

    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code>AbstractLoadBalance</code> 的实现类有下面这些：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201220089.png" alt="image-20220620122010926" loading="lazy"></p><p>RandomLoadBalance（默认）：权重随机选择（对加权随机算法的实现）</p><ul><li>假如有两个提供相同服务的服务器 S1,S2，S1的权重为7，S2的权重为3。我们把这些权重值分布在坐标区间会得到：S1-&gt;[0, 7) ，S2-&gt;[7, 10)。我们生成[0, 10) 之间的随机数，随机数落到对应的区间，我们就选择对应的服务器来处理请求。</li></ul><p>LeastActiveLoadBalance：最小活跃数负载均衡。</p><ul><li>初始状态下所有服务提供者的活跃数均为 0（每个服务提供者的中特定方法都对应一个活跃数，我在后面的源码中会提到），每收到一个请求后，对应的服务提供者的活跃数 +1，当这个请求处理完之后，活跃数 -1。</li><li><strong>Dubbo 就认为谁的活跃数越少，谁的处理速度就越快，性能也越好，这样的话，我就优先把请求给活跃数少的服务提供者处理。</strong></li><li>**如果有多个服务提供者的活跃数相等怎么办？**很简单，那就再走一遍 <code>RandomLoadBalance</code> 。</li></ul><p>ConsistentHashLoadBalance：一致性Hash负载均衡策略。</p><ul><li><code>ConsistentHashLoadBalance</code> 中没有权重的概念，<strong>具体是哪个服务提供者处理请求是由你的请求的参数决定</strong>的，也就是说<strong>相同参数的请求总是发到同一个服务提供者</strong>。</li><li>Dubbo 为了避免数据倾斜问题（节点不够分散，大量请求落到同一节点），还引入了虚拟节点的概念。通过虚拟节点可以让节点更加分散，有效均衡各个节点的请求量。</li></ul><p>RoundRobinLoadBalance：加权轮询负载均衡。</p><ul><li><p>轮询就是把请求依次分配给每个服务提供者。加权轮询就是在轮询的基础上，让更多的请求落到权重更大的服务提供者上。比如假如有两个提供相同服务的服务器 S1,S2，S1的权重为7，S2的权重为3。</p><p>如果我们有 10 次请求，那么 7 次会被 S1处理，3次被 S2处理。</p></li></ul><h5 id="_8、序列化方式" tabindex="-1"><a class="header-anchor" href="#_8、序列化方式" aria-hidden="true">#</a> 8、序列化方式</h5><p>Dubbo 支持多种序列化方式：JDK自带的序列化、hessian2、JSON、Kryo、FST、Protostuff，ProtoBuf等等。</p><p>Dubbo 默认使用的序列化方式是 hessian2。</p><h5 id="_9、谈谈你对这些序列化协议了解" tabindex="-1"><a class="header-anchor" href="#_9、谈谈你对这些序列化协议了解" aria-hidden="true">#</a> 9、谈谈你对这些序列化协议了解？</h5><p>一般我们不会直接使用 JDK 自带的序列化方式。主要原因有两个：</p><ol><li><strong>不支持跨语言调用</strong> : 如果调用的是其他语言开发的服务的时候就不支持了。</li><li><strong>性能差</strong> ：相比于其他序列化框架性能更低，主要原因是序列化之后的字节数组体积较大，导致传输成本加大。</li></ol><p>JSON 序列化由于性能问题，我们一般也不会考虑使用。</p><p>像 Protostuff，ProtoBuf、hessian2这些都是跨语言的序列化方式，如果有跨语言需求的话可以考虑使用。</p><p>Kryo和FST这两种序列化方式是 Dubbo 后来才引入的，性能非常好。不过，这两者都是专门针对 Java 语言的。Dubbo 官网的一篇文章中提到说推荐使用 Kryo 作为生产环境的序列化方式。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201230787.png" alt="image-20220620123012722" loading="lazy"></p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201230409.png" alt="image-20220620123038334" loading="lazy"> <img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201230863.png" alt="image-20220620123046789" loading="lazy"></p><h3 id="服务之间的调用为啥不直接用-http-而用-rpc" tabindex="-1"><a class="header-anchor" href="#服务之间的调用为啥不直接用-http-而用-rpc" aria-hidden="true">#</a> 服务之间的调用为啥不直接用 HTTP 而用 RPC？</h3><p>RPC 只是一种概念、一种设计，就是为了解决 <strong>不同服务之间的调用问题</strong>, 它一般会包含有 <strong>传输协议</strong> 和 <strong>序列化协议</strong> 这两个。</p><p>但是，HTTP 是一种协议，RPC框架可以使用 HTTP协议作为传输协议或者直接使用TCP作为传输协议，使用不同的协议一般也是为了适应不同的场景。</p><p>从上面对 RPC 介绍的内容中，概括来讲RPC 主要解决了：<strong>让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单。</strong></p><h2 id="_8、分布式事务" tabindex="-1"><a class="header-anchor" href="#_8、分布式事务" aria-hidden="true">#</a> 8、分布式事务</h2><h3 id="_1、基础理论" tabindex="-1"><a class="header-anchor" href="#_1、基础理论" aria-hidden="true">#</a> 1、基础理论</h3><h4 id="柔性事务" tabindex="-1"><a class="header-anchor" href="#柔性事务" aria-hidden="true">#</a> 柔性事务</h4><p>互联网应用最关键的就是要保证高可用， 计算式系统几秒钟之内没办法使用都有可能造成数百万的损失。在此场景下，一些大佬们在 CAP 理论和 BASE 理论的基础上，提出了 柔性事务 的概念。 柔性事务追求的是最终一致性。</p><p>实际上，柔性事务就是 BASE 理论 +业务实践。 柔性事务追求的目标是：我们根据自身业务特性，<strong>通过适当的方式来保证系统数据的最终一致性</strong>。 像 TCC、 Saga、MQ 事务 、本地消息表 就属于柔性事务。</p><h4 id="刚性事务" tabindex="-1"><a class="header-anchor" href="#刚性事务" aria-hidden="true">#</a> 刚性事务</h4><p>与柔性事务相对的就是 刚性事务 了。前面我们说了，柔性事务追求的是最终一致性 。那么，与之对应，刚性事务追求的就是 <strong>强一致性</strong>。像2PC 、3PC 就属于刚性事务。</p><h3 id="_2、分布式事务解决方案" tabindex="-1"><a class="header-anchor" href="#_2、分布式事务解决方案" aria-hidden="true">#</a> 2、分布式事务解决方案</h3><p>分布式事务的解决方案有很多，比如：<strong>2PC、3PC、TCC、本地消息表、MQ 事务</strong>（Kafka 和 RocketMQ 都提供了事务相关功能） 、Saga 等等。这些方案的适用场景有所区别，我们需要根据具体的场景选择适合自己项目的解决方案。</p><h4 id="_2pc-两阶段提交协议-强一致性" tabindex="-1"><a class="header-anchor" href="#_2pc-两阶段提交协议-强一致性" aria-hidden="true">#</a> 2PC（两阶段提交协议，强一致性）</h4><p>2PC（Two-Phase Commit）这三个字母的含义:</p><ul><li>2 -&gt; 指代事务提交的 2 个阶段</li><li>P-&gt; Prepare (准备阶段)</li><li>C -&gt;Commit（提交阶段）</li></ul><p>2PC 将事务的提交过程分为 2 个阶段：准备阶段 和 提交阶段 。</p><blockquote><p>准备阶段(Prepare)</p></blockquote><p>准备阶段的核心是“询问”事务参与者执行本地数据库事务操作是否成功。</p><p>1、事务协调者/管理者 向所有参与者发送消息询问：“你是否可以执行事务操作呢？”，并等待其答复。</p><p>2、事务参与者 接收到消息之后，开始执行本地数据库事务预操作比如写 redo log/undo log 日志。但是 ，此时并不会提交事务！</p><p>3、事务参与者 如果执行本地数据库事务操作成功，那就回复：“就绪”，否则就回复：“未就绪”。</p><blockquote><p>提交阶段(Commit)</p></blockquote><p>提交阶段的核心是“询问”事务参与者提交事务是否成功。</p><p>当所有事务参与者都是“就绪”状态的话：</p><p>1、事务协调者/管理者 向所有参与者发送消息：“你们可以提交事务啦！”（commit 消息）</p><p>2、事务参与者 接收到 commit 消息 后执行 提交本地数据库事务 操作，执行完成之后 释放整个事务期间所占用的资源。</p><p>3、事务参与者 回复：“事务已经提交” （ack 消息）。</p><p>4、事务协调者/管理者 收到所有 事务参与者 的 ack 消息 之后，整个分布式事务过程正式结束。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201241323.png" alt="image-20220620124100245" loading="lazy"></p><p>当任一事务参与者是“未就绪”状态的话：</p><p>1、事务协调者/管理者 向所有参与者发送消息：“你们可以执行回滚操作了！”（rollback 消息）。</p><p>2、事务参与者 接收到 rollback 消息 后执行 本地数据库事务回滚 执行完成之后 释放整个事务期间所占用的资源。</p><p>3、事务参与者 回复：“事务已经回滚” （ack 消息）。</p><p>4、事务协调者/管理者 收到所有 事务参与者 的 ack 消息 之后，取消事务。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201241371.png" alt="image-20220620124137300" loading="lazy"></p><h5 id="总结-2" tabindex="-1"><a class="header-anchor" href="#总结-2" aria-hidden="true">#</a> 总结</h5><p>简单总结一下 2PC 两阶段中比较重要的一些点：</p><p>1、准备阶段 的主要目的是测试 事务参与者 能否执行 本地数据库事务 操作（!!!注意：这一步并不会提交事务）。 2、提交阶段 中 事务协调者/管理者 会根据 准备阶段 中 事务参与者 的消息来决定是执行事务提交还是回滚操作。 3、提交阶段 之后一定会结束当前的分布式事务</p><p>2PC 的优点：</p><ul><li>实现起来非常简单，各大主流数据库比如 MySQL、Oracle 都有自己实现。</li><li>针对的是<strong>数据强一致性</strong>。不过，仍然可能存在数据不一致的情况。</li></ul><p>2PC 存在的问题：</p><ul><li>同步阻塞 ：事务参与者会在正式提交事务之前会一直占用相关的资源。比如用户小明转账给小红，那其他事务也要操作用户小明或小红的话，就会阻塞。</li><li>数据不一致 ：由于网络问题或者事务协调者/管理者宕机都有可能会造成数据不一致的情况。比如在第2阶段（提交阶段），部分网络出现问题导致部分参与者收不到 commit/rollback 消息的话，就会导致数据不一致。</li><li>单点问题 ： 事务协调者/管理者在其中也是一个很重要的角色，如果事务协调者/管理者在准备(Prepare)阶段完成之后挂掉的话，事务参与者就会一直卡在提交(Commit)阶段。</li></ul><h4 id="_3pc-三阶段提交协议" tabindex="-1"><a class="header-anchor" href="#_3pc-三阶段提交协议" aria-hidden="true">#</a> 3PC（三阶段提交协议）</h4><p>3PC 是人们在 2PC 的基础上做了一些优化得到的。3PC 把 2PC 中的 准备阶段(Prepare) 做了进一步细化，分为 2 个阶段：</p><ul><li>询问阶段(CanCommit) ：这一步 不会执行事务操作，只会询问事务参与者能否执行本地数据库事操作。</li><li>准备阶段(PreCommit) ：当所有事物参与者都返回“可执行”之后， 事务参与者才会执行本地数据库事务预操作比如写 redo log/undo log 日志。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201244562.png" alt="image-20220620124426484" loading="lazy"></p><p>除此之外，3PC 还引入了 超时机制 来避免事务参与者一直阻塞占用资源。</p><h4 id="tcc-补偿事务—最终一致性-即柔性事务" tabindex="-1"><a class="header-anchor" href="#tcc-补偿事务—最终一致性-即柔性事务" aria-hidden="true">#</a> TCC（补偿事务—最终一致性，即柔性事务）</h4><p>TCC 属于目前比较火的一种柔性事务解决方案。TCC 这个概念最早诞生于数据库专家帕特 · 赫兰德（Pat Helland）于 2007 发表的 <a href="https://www.ics.uci.edu/~cs223/papers/cidr07p15.pdf" target="_blank" rel="noopener noreferrer">《Life beyond Distributed Transactions: an Apostate’s Opinion》<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 这篇论文，感兴趣的小伙伴可以阅读一下这篇论文。</p><p>简单来说，TCC 是 Try、Confirm、Cancel 三个词的缩写，它分为三个阶段：</p><p>1、Try（尝试）阶段 : 尝试执行。完成业务检查，并预留好必需的业务资源。</p><p>2、Confirm（确认）阶段 ：确认执行。当所有事务参与者的 Try 阶段执行成功就会执行 Confirm ，Confirm 阶段会处理 Try 阶段预留的业务资源。否则，就会执行 Cancel 。</p><p>3、Cancel（取消）阶段 ：取消执行，释放 Try 阶段预留的业务资源。</p><p>我们拿转账场景来说：</p><p>1、Try（尝试）阶段 : 在转账场景下，Try 要做的事情是就是检查账户余额是否充足，预留的资源就是转账资金。</p><p>2、Confirm（确认）阶段 ： 如果 Try 阶段执行成功的话，Confirm 阶段就会执行真正的扣钱操作。</p><p>3、Cancel（取消）阶段 ：释放 Try 阶段预留的转账资金。</p><p>一般情况下，当我们使用TCC模式的时候,需要自己实现 try, confirm, cancel 这三个方法，来达到<strong>最终一致性</strong>。也就是说，正常情况下会执行 try, confirm，如下图所示。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201246315.png" alt="image-20220620124648224" loading="lazy"></p><p>出现异常的话会执行 try, cancel ，如下图所示。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201246317.png" alt="image-20220620124656230" loading="lazy"></p><p>因此，TCC 模式不需要依赖于底层数据资源的事务支持，但是需要我们手动实现更多的代码，属于 侵入业务代码 的一种分布式解决方案。</p><p>针对 TCC 的实现，业界也有一些不错的开源框架。不同的框架对于 TCC 的实现可能略有不同，不过大致思想都一样。</p><p>1、 : ByteTCC 是基于 Try-Confirm-Cancel（TCC）机制的分布式事务管理器的实现。 相关阅读：<a href="https://www.bytesoft.org/how-to-impl-tcc/" target="_blank" rel="noopener noreferrer">关于如何实现一个 TCC 分布式事务框架的一点思考<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></p><p>2、<a href="https://seata.io/zh-cn/index.html" target="_blank" rel="noopener noreferrer">Seata<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> :Seata 是一款开源的分布式事务解决方案，致力于在微服务架构下提供高性能和简单易用的分布式事务服务。</p><p>3、<a href="https://gitee.com/shuaiqiyu/hmily" target="_blank" rel="noopener noreferrer">Hmily<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> : 金融级分布式事务解决方案。</p><h4 id="mq-事务-消息队列-采用两阶段提交-2pc" tabindex="-1"><a class="header-anchor" href="#mq-事务-消息队列-采用两阶段提交-2pc" aria-hidden="true">#</a> MQ 事务（消息队列，采用两阶段提交（2PC））</h4><p>RocketMQ 、 Kafka、Pulsar 、QMQ都提供了事务相关的功能。事务允许事件流应用将消费，处理，生产消息整个过程定义为一个原子操作。</p><p>这里我们拿 RocketMQ 来说（图源：《消息队列高手课》）。相关阅读：<a href="https://rocketmq.apache.org/docs/transaction-example/" target="_blank" rel="noopener noreferrer">RocketMQ 事务消息参考文档<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201248096.png" alt="image-20220620124808013" loading="lazy"></p><p>1、MQ 发送方（比如物流服务）在消息队列上开启一个事务，然后发送一个“半消息”给 MQ Server/Broker。事务提交之前，半消息对于 MQ 订阅方/消费者（比如第三方通知服务）不可见</p><p>2、“半消息”发送成功的话，MQ 发送方就开始执行本地事务。</p><p>3、MQ 发送方的本地事务执行成功的话，“半消息”变成正常消息，可以正常被消费。MQ 发送方的本地事务执行失败的话，会直接回滚。</p><p>从上面的流程中可以看出，MQ 的事务消息使用的是<strong>两阶段提交（2PC）</strong>，简单来说就是咱先发送半消息，等本地事务执行成功之后，半消息才变为正常消息。</p><p><strong>如果 MQ 发送方提交或者回滚事务消息时失败怎么办？</strong></p><ul><li>RocketMQ 中的 Broker 会定期去 MQ 发送方上反查这个事务的本地事务的执行情况，并根据反查结果决定提交或者回滚这个事务。</li><li>事务反查机制的实现依赖于我们业务代码实现的对应的接口，比如你要查看创建物流信息的本地事务是否执行成功的话，直接在数据库中查询对应的物流信息是否存在即可。</li></ul><p><strong>如果正常消息没有被正确消费怎么办呢？</strong></p><p>消息消费失败的话，RocketMQ 会自动进行消费重试。如果超过最大重试次数这个消息还是没有正确消费，RocketMQ 就会认为这个消息有问题，然后将其放到 死信队列。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201249855.png" alt="image-20220620124954784" loading="lazy"></p><p>进入死信队列的消费一般需要人工处理，手动排查问题。</p><p>QMQ 的事务消息就没有 RocketMQ 实现的那么复杂了，它借助了数据库自带的事务功能。其核心思想其实就是 eBay 提出的 本地消息表 方案，将分布式事务拆分成本地事务进行处理。</p><p>我们维护一个本地消息表用来存放消息发送的状态，保存消息发送情况到本地消息表的操作和业务操作要在一个事务里提交。这样的话，业务执行成功代表消息表也写入成功。</p><p>然后，我们再单独起一个线程定时轮询消息表，把没处理的消息发送到消息中间件。</p><p>消息发送成功后，更新消息状态为成功或者直接删除消息。</p><p>RocketMQ 的事务消息方案中，如果消息队列挂掉，数据库事务就无法执行了，整个应用也就挂掉了。</p><p>QMQ 的事务消息方案中，即使消息队列挂了也不会影响数据库事务的执行。</p><p>因此，QMQ 实现的方案能更加适应于大多数业务。不过，这种方法同样适用于其他消息队列，只能说 QMQ 封装的更好，开箱即用罢了！</p><p>相关阅读： <a href="https://mp.weixin.qq.com/s/cBx1l1zaThN6_808fMl27g" target="_blank" rel="noopener noreferrer">面试官：RocketMQ 分布式事务消息的缺点？<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></p><h4 id="saga" tabindex="-1"><a class="header-anchor" href="#saga" aria-hidden="true">#</a> Saga</h4><p>Saga 绝对可以说是历史非常悠久了，Saga 事务理论在 1987 年 Hector &amp; Kenneth 在 ACM 发表的论文 <a href="https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf" target="_blank" rel="noopener noreferrer">《Sagas》<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 中就被提出了，早于分布式事务概念的提出。</p><p>Saga 属于长事务解决方案，其核心思想史将长事务拆分为多个本地短事务（本地短事务序列）。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201250060.png" alt="image-20220620125042987" loading="lazy"></p><ul><li>长事务 —&gt; T1,T2 ~ Tn 个本地短事务</li><li>每个短事务都有一个补偿动作 —&gt; C1,C2 ~ Cn</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201251612.png" alt="image-20220620125104538" loading="lazy"></p><p>如果 T1,T2 ~ Tn 这些短事务都能顺利完成的话，整个事务也就顺利结束，否则，将采取恢复模式。</p><p>**反向恢复 ：**补偿</p><ul><li>简介：如果 Ti 短事务提交失败，则补偿所有已完成的事务（一直执行 Ci 对 Ti 进行补偿）。</li><li>执行顺序：T1，T2，…，Ti（失败），Ci（补偿），…，C2，C1。</li></ul><p>**正向恢复 ：**重试</p><ul><li>简介：如果 Ti 短事务提交失败，则一直对 Ti 进行重试，直至成功为止。</li><li>执行顺序：T1，T2，…，Ti（失败），Ti（重试）…，Ti+1，…，Tn。</li></ul><p>和 TCC 类似，Saga 正向操作与补偿操作都需要业务开发者自己实现，因此也属于 侵入业务代码 的一种分布式解决方案。和 TCC 很大的一点不同是 Saga 没有“Try” 动作，它的本地事务 Ti 直接被提交。因此，性能非常高！</p><p>理论上来说，补偿操作一定能够执行成功。不过，当网络出现问题或者服务器宕机的话，补偿操作也会执行失败。这种情况下，往往需要我们进行人工干预。并且，为了能够提高容错性（比如 Saga 系统本身也可能会崩溃），保证所有的短事务都得以提交或补偿，我们还需要将这些操作通过日志记录下来（Saga log，类似于数据库的日志机制）。这样，Saga 系统恢复之后，我们就知道短事务执行到哪里了或者补偿操作执行到哪里了。</p><p>另外，因为 Saga 没有进行“Try” 动作预留资源，所以不能保证隔离性。这也是 Saga 比较大的一个缺点。</p><p>针对 Saga 的实现，业界也有一些不错的<strong>开源框架</strong>。不同的框架对于 Saga 的实现可能略有不同，不过大致思想都一样。</p><p>1、<a href="https://github.com/apache/servicecomb-pack" target="_blank" rel="noopener noreferrer">ServiceComb Pack<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> ：微服务应用的数据最终一致性解决方案。</p><p>2、<a href="https://seata.io/zh-cn/index.html" target="_blank" rel="noopener noreferrer">Seata<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> :Seata 是一款开源的分布式事务解决方案，致力于在微服务架构下提供高性能和简单易用的分布式事务服务。</p><h2 id="_9、分布式协调" tabindex="-1"><a class="header-anchor" href="#_9、分布式协调" aria-hidden="true">#</a> 9、分布式协调</h2><h3 id="zookeeper" tabindex="-1"><a class="header-anchor" href="#zookeeper" aria-hidden="true">#</a> Zookeeper</h3><p>1、Zookeeper集群的脑裂问题以及解决方案？</p><p>对于一个集群，通常多台机器会部署在不同机房，来提高这个集群的可用性。保证可用性的同时，会发生一种机房间网络线路故障，导致机房间网络不通，而集群被割裂成几个小集群。这时候子集群各自选主导致“脑裂”的情况。</p><p>举例说明：比如现在有一个由 6 台服务器所组成的一个集群，部署在了 2 个机房，每个机房 3 台。正常情况下只有 1 个 leader，但是当两个机房中间网络断开的时候，每个机房的 3 台服务器都会认为另一个机房的 3 台服务器下线，而选出自己的 leader 并对外提供服务。若没有过半机制，当网络恢复的时候会发现有 2 个 leader。仿佛是 1 个大脑（leader）分散成了 2 个大脑，这就发生了脑裂现象。脑裂期间 2 个大脑都可能对外提供了服务，这将会带来数据一致性等问题。</p><p>解决方案：采用过半机制。ZooKeeper 的过半机制导致不可能产生 2 个 leader，因为少于等于一半是不可能产生 leader 的，这就使得不论机房的机器如何分配都不可能发生脑裂。</p><p>2、ZAB协议</p><p><code>ZAB(ZooKeeper Atomic Broadcast)</code> 原子广播协议，该协议能够很好地支持 <strong>崩溃恢复</strong> 。</p><p>和介绍 <code>Paxos</code> 一样，在介绍 <code>ZAB</code> 协议之前，我们首先来了解一下在 <code>ZAB</code> 中三个主要的角色，<code>Leader 领导者</code>、<code>Follower跟随者</code>、<code>Observer观察者</code> 。</p><ul><li><code>Leader</code> ：集群中 <strong>唯一的写请求处理者</strong> ，能够发起投票（投票也是为了进行写请求）。</li><li><code>Follower</code>：能够接收客户端的请求，如果是读请求则可以自己处理，<strong>如果是写请求则要转发给 <code>Leader</code></strong> 。在选举过程中会参与投票，<strong>有选举权和被选举权</strong> 。</li><li><code>Observer</code> ：就是没有选举权和被选举权的 <code>Follower</code> 。</li></ul><p>3、 <strong>崩溃恢复</strong> 是什么玩意？</p><ul><li>当集群中有机器挂了，我们整个集群如何保证数据一致性？</li></ul><p>其实主要就是 <strong>当集群中有机器挂了，我们整个集群如何保证数据一致性？</strong></p><ul><li>如果只是 <code>Follower</code> 挂了，而且挂的没超过半数的时候，因为我们一开始讲了在 <code>Leader</code> 中会维护队列，所以不用担心后面的数据没接收到导致数据不一致性。</li></ul><p>如果 <code>Leader</code> 挂了那就麻烦了?</p><ul><li>我们肯定需要先暂停服务变为 <code>Looking</code> 状态然后进行 <code>Leader</code> 的重新选举（上面我讲过了），但这个就要分为两种情况了，分别是 <strong>确保已经被Leader提交的提案最终能够被所有的Follower提交</strong> 和 <strong>跳过那些已经被丢弃的提案</strong> 。</li></ul><p>确保已经被Leader提交的提案最终能够被所有的Follower提交是什么意思呢？</p><ul><li>假设 <code>Leader (server2)</code> 发送 <code>commit</code> 请求（忘了请看上面的消息广播模式），他发送给了 <code>server3</code>，然后要发给 <code>server1</code> 的时候突然挂了。这个时候重新选举的时候我们如果把 <code>server1</code> 作为 <code>Leader</code> 的话，那么肯定会产生数据不一致性，因为 <code>server3</code> 肯定会提交刚刚 <code>server2</code> 发送的 <code>commit</code> 请求的提案，而 <code>server1</code> 根本没收到所以会丢弃。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201338706.png" alt="image-20220620133843603" loading="lazy"></p><p>那怎么解决呢？</p><ul><li>聪明的同学肯定会质疑，<strong>这个时候 <code>server1</code> 已经不可能成为 <code>Leader</code> 了，因为 <code>server1</code> 和 <code>server3</code> 进行投票选举的时候会比较 <code>ZXID</code> ，而此时 <code>server3</code> 的 <code>ZXID</code> 肯定比 <code>server1</code> 的大了</strong>。(不理解可以看前面的选举算法)</li></ul><p>那么跳过那些已经被丢弃的提案又是什么意思呢？</p><ul><li>假设 <code>Leader (server2)</code> 此时同意了提案N1，自身提交了这个事务并且要发送给所有 <code>Follower</code> 要 <code>commit</code> 的请求，却在这个时候挂了，此时肯定要重新进行 <code>Leader</code> 的选举，比如说此时选 <code>server1</code> 为 <code>Leader</code> （这无所谓）。但是过了一会，这个 <strong>挂掉的 <code>Leader</code> 又重新恢复了</strong> ，此时它肯定会作为 <code>Follower</code> 的身份进入集群中，需要注意的是刚刚 <code>server2</code> 已经同意提交了提案N1，但其他 <code>server</code> 并没有收到它的 <code>commit</code> 信息，所以其他 <code>server</code> 不可能再提交这个提案N1了，这样就会出现数据不一致性问题了，所以 <strong>该提案N1最终需要被抛弃掉</strong> 。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201338264.png" alt="image-20220620133856163" loading="lazy"></p><hr><h1 id="高性能" tabindex="-1"><a class="header-anchor" href="#高性能" aria-hidden="true">#</a> 高性能</h1><h2 id="读写分离" tabindex="-1"><a class="header-anchor" href="#读写分离" aria-hidden="true">#</a> 读写分离</h2><h3 id="_1、产生的问题以及解决方案" tabindex="-1"><a class="header-anchor" href="#_1、产生的问题以及解决方案" aria-hidden="true">#</a> 1、产生的问题以及解决方案</h3><p>目的：读写分离主要是为了将对数据库的读写操作分散到不同的数据库节点上。</p><p>效果：小幅提升写性能，大幅提升读性能。</p><p>选择模式：选择<strong>一主多从</strong>，也就是<strong>一台主数据库负责写</strong>，其他的从数据库负责读。主库和从库之间会进行数据同步，以保证从库中数据的准确性。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201419219.png" alt="image-20220620141902118" loading="lazy"></p><p><strong>读写分离会带来的问题</strong>？<strong>如何解决</strong>？</p><p>容易引发问题：主库和从库的数据存在延迟，比如你写完主库之后，主库的数据同步到从库是需要时间的，这个时间差就导致了主库和从库的数据不一致性问题。这也就是我们经常说的 <strong>主从同步延迟</strong> 。</p><p><strong>解决方案</strong>：</p><p>1、强制将读请求路由到主库处理</p><p>既然你从库的数据过期了，那我就直接从主库读取嘛！这种方案虽然会增加主库的压力，但是，实现起来比较简单。</p><p>方案：<code>Sharding-JDBC</code> 就是采用的这种方案。通过使用 Sharding-JDBC 的 <code>HintManager</code> 分片键值管理器，我们可以强制使用主库。</p><ul><li>对于这种方案，你可以将那些必须获取最新数据的读请求都交给主库处理。</li></ul><p>2、延迟读取</p><p>思路：既然主从同步存在延迟，那我就在延迟之后读取啊，比如主从同步延迟 0.5s,那我就 1s 之后再读取数据。</p><p>方案：对于一些对数据比较敏感的场景，你可以在完成写请求之后，避免立即进行请求操作。比如你<strong>支付成功之后，跳转到一个支付成功的页面，当你点击返回之后才返回自己的账户</strong>。</p><h3 id="_2、-如何实现读写分离" tabindex="-1"><a class="header-anchor" href="#_2、-如何实现读写分离" aria-hidden="true">#</a> 2、 如何实现读写分离？</h3><blockquote><p>步骤</p></blockquote><p>实现读写分离步骤：</p><p>1、部署多台数据库，选择其中的一台作为主数据库，其他的一台或者多台作为从数据库。</p><p>2、保证主数据库和从数据库之间的数据是实时同步的，这个过程也就是我们常说的主从复制。</p><p>3、系统将写请求交给主数据库处理，读请求交给从数据库处理。</p><blockquote><p>落实项目的常用方式</p></blockquote><p>1、代理方式</p><p>原理：在应用和数据中间<strong>加了一个代理层</strong>。应用程序所有的数据请求都交给代理层处理，代理层负责分离读写请求，将它们路由到对应的数据库中。</p><p>可用产品：提供类似功能的中间件有 <strong>MySQL Router</strong>（官方）、<strong>Atlas</strong>（基于 MySQL Proxy）、<strong>Maxscale</strong>、<strong>MyCat</strong>。</p><p>2、组件方式（这种方式目前在各种互联网公司中用的最多的）</p><p>思路：通过引入第三方组件来帮助我们读写请求</p><p>可用产品：推荐使用 <code>sharding-jdbc</code> ，直接引入 jar 包即可使用，非常方便。同时，也节省了很多运维的成本。</p><h3 id="_3、主从复制原理了解么" tabindex="-1"><a class="header-anchor" href="#_3、主从复制原理了解么" aria-hidden="true">#</a> 3、主从复制原理了解么？</h3><p>MySQL binlog(binary log 即二进制日志文件) 主要记录了 MySQL 数据库中数据的所有变化(数据库执行的所有 DDL 和 DML 语句)。因此，我们根据主库的 MySQL binlog 日志就能够将主库的数据同步到从库中。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201428897.png" alt="image-20220620142816788" loading="lazy"></p><ol><li>主库将数据库中数据的变化写入到 binlog</li><li>从库连接主库</li><li>从库会创建一个 I/O 线程向主库请求更新的 binlog</li><li>主库会创建一个 binlog dump 线程来发送 binlog ，从库中的 I/O 线程负责接收</li><li>从库的 I/O 线程将接收的 binlog 写入到 relay log 中。</li><li>从库的 SQL 线程读取 relay log 同步数据本地（也就是再执行一遍 SQL ）。</li></ol><p><strong>扩展</strong>：阿里开源的一个叫做 <code>canal</code> 的工具。这个工具可以帮助我们实现 <code>MySQL </code>和其他数据源比如<code>Elasticsearch</code>或者另外一台 MySQL 数据库之间的数据同步。很显然，这个工具的底层原理肯定也是依赖 binlog。canal 的原理就是模拟 MySQL 主从复制的过程，解析 binlog 将数据同步到其他的数据源。</p><p>另外，像咱们常用的分布式缓存组件 Redis 也是通过主从复制实现的读写分离。</p><h3 id="总结-3" tabindex="-1"><a class="header-anchor" href="#总结-3" aria-hidden="true">#</a> 总结</h3><p>MySQL 主从复制是依赖于 <code>binlog</code> 。另外，常见的一些同步 MySQL 数据到其他数据源的工具（比如 canal）的底层一般也是依赖 binlog 。</p><h2 id="分库分表" tabindex="-1"><a class="header-anchor" href="#分库分表" aria-hidden="true">#</a> 分库分表</h2><h3 id="_1、理解分库分表" tabindex="-1"><a class="header-anchor" href="#_1、理解分库分表" aria-hidden="true">#</a> 1、理解分库分表</h3><p><strong>问题引出</strong>：读写分离主要应对的是数据库读并发，没有解决数据库存储问题。如果 MySQL 一张表的数据量过大怎么办?我们该如何解决 MySQL 的存储压力呢？</p><p><strong>解决方案</strong>：<code>分库分表</code>。</p><blockquote><p>什么是分库？</p></blockquote><p>解释：分库就是将数据库中的数据分散到不同的数据库上。</p><p>案例介绍：无论是多表或者说单表水平拆分，都最终放在了不同的数据库。</p><ul><li>你将数据库中的用户表和用户订单表<strong>分别放在两个不同的数据库</strong>。</li><li>由于用户表数据量太大，你对用户表进行了水平切分，然后将切分后的 2 张用户表<strong>分别放在两个不同的数据库</strong>。</li></ul><blockquote><p>何为分表？</p></blockquote><p>解释：分表就是对<strong>单表</strong>的数据进行拆分，<strong>可以是垂直拆分，也可以是水平拆分</strong>。</p><p><strong>何为垂直拆分？</strong></p><ul><li>垂直拆分是对数据表列的拆分，把一张列比较多的表拆分为多张表。</li><li>举个例子：我们可以将用户信息表中的一些列单独抽出来作为一个表。</li></ul><p><strong>何为水平拆分？</strong></p><ul><li>水平拆分是对数据表行的拆分，把一张行比较多的表拆分为多张表。</li><li>举个例子：我们可以将<strong>用户信息表拆分成多个用户信息表</strong>，这样就可以避免单一表数据量过大对性能造成影响。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201435055.png" alt="image-20220620143537954" loading="lazy"></p><h3 id="_2、什么情况下需要分库分表" tabindex="-1"><a class="header-anchor" href="#_2、什么情况下需要分库分表" aria-hidden="true">#</a> 2、什么情况下需要分库分表？</h3><p>分库：从整个数据库来看</p><ul><li>数据库中的数据占用的空间越来越大，备份时间越来越长（分库）。</li><li>应用的并发量太大（分库）</li></ul><p>分表：单表数据量大</p><ul><li>单表的数据达到千万级别以上，数据库读写速度比较缓慢（分表）。</li></ul><h3 id="_3、分库分表会带来什么问题呢" tabindex="-1"><a class="header-anchor" href="#_3、分库分表会带来什么问题呢" aria-hidden="true">#</a> 3、分库分表会带来什么问题呢？</h3><blockquote><p>记住，你在公司做的任何技术决策，不光是要考虑这个技术能不能满足我们的要求，是否适合当前业务场景，还要重点考虑其带来的成本。</p></blockquote><p>引入分库分表之后，会给系统带来什么挑战呢？</p><p><strong>join 操作</strong> （原本join替换为自己手动封装）：同一个数据库中的表分布在了不同的数据库中，导致无法使用 join 操作。这样就导致我们需要手动进行数据的封装，比如你在一个数据库中查询到一个数据之后，再根据这个数据去另外一个数据库中找对应的数据。</p><p><strong>事务问题</strong> （分布式事务）：同一个数据库中的表分布在了不同的数据库中，如果单个操作涉及到多个数据库，那么数据库自带的事务就无法满足我们的要求了。</p><p><strong>分布式 id</strong> ：分库之后， 数据遍布在不同服务器上的数据库，数据库的自增主键已经没办法满足生成的主键唯一了。我们如何为不同的数据节点生成全局唯一主键呢？这个时候，我们就需要为我们的系统引入分布式 id 了。</p><p><strong>其他问题</strong>：引入分库分表之后，一般需要 DBA 的参与，同时还需要更多的数据库服务器，这些都属于成本。</p><h3 id="_4、分库分表有没有什么比较推荐的方案" tabindex="-1"><a class="header-anchor" href="#_4、分库分表有没有什么比较推荐的方案" aria-hidden="true">#</a> 4、分库分表有没有什么比较推荐的方案？</h3><p>ShardingSphere 项目（包括 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar）是当当捐入 Apache 的，目前主要由京东数科的一些巨佬维护。</p><p>核心功能如下：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201441189.png" alt="image-20220620144148061" loading="lazy"></p><p>ShardingSphere 绝对可以说是当前分库分表的首选！ShardingSphere 的功能完善，除了支持读写分离和分库分表，还提供分布式事务、数据库治理等功能。</p><p>艿艿之前写了一篇分库分表的实战文章，各位朋友可以看看：<a href="https://mp.weixin.qq.com/s/A2MYOFT7SP-7kGOon8qJaw" target="_blank" rel="noopener noreferrer">《芋道 Spring Boot 分库分表入门》open in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 。</p><h3 id="_5、分库分表后-数据怎么迁移呢" tabindex="-1"><a class="header-anchor" href="#_5、分库分表后-数据怎么迁移呢" aria-hidden="true">#</a> 5、分库分表后，数据怎么迁移呢？</h3><p><strong>停机迁移方案</strong>：比较简单同时也是非常常用的方案就是<strong>停机迁移</strong>，写个脚本老库的数据写到新库中。比如你在凌晨 2 点，系统使用的人数非常少的时候，挂一个公告说系统要维护升级预计 1 小时。然后，你写一个脚本将老库的数据都同步到新库中。</p><p><strong>双写方案</strong>（针对那种不能停机迁移的场景）：</p><p>1、我们对老库的更新操作（增删改），同时也要写入新库（双写）。如果操作的数据不存在于新库的话，需要插入到新库中。 这样就能保证，咱们新库里的数据是最新的。</p><p>2、在迁移过程，双写只会让被更新操作过的老库中的数据同步到新库，我们还需要自己写脚本将老库中的数据和新库的数据做比对。如果新库中没有，那咱们就把数据插入到新库。如果新库有，旧库没有，就把新库对应的数据删除（冗余数据清理）。</p><p>3、重复上一步的操作，直到老库和新库的数据一致为止。</p><p><strong>实际问题阐述</strong>：想要在项目中实施双写还是比较麻烦的，很容易会出现问题。我们可以借助上面提到的数据库同步工具 <code>Canal </code>做增量数据迁移（<strong>还是依赖 binlog，开发和维护成本较低</strong>）。</p><h2 id="负载均衡" tabindex="-1"><a class="header-anchor" href="#负载均衡" aria-hidden="true">#</a> 负载均衡</h2><p>负载均衡系统通常用于将任务比如用户<strong>请求处理分配到多个服务器处理以提高网站、应用或者数据库的性能和可靠性</strong>。</p><p>常见的负载均衡系统包括 3 种：</p><ol><li><strong>DNS 负载均衡</strong> ：一般用来实现地理级别的均衡。</li><li><strong>硬件负载均衡</strong> ： 通过单独的硬件设备比如 F5 来实现负载均衡功能（硬件的价格一般很贵）。</li><li><strong>软件负载均衡</strong> ：通过负载均衡软件比如 Nginx 来实现负载均衡功能。</li></ol><h2 id="消息队列" tabindex="-1"><a class="header-anchor" href="#消息队列" aria-hidden="true">#</a> 消息队列</h2><p>常用消息队列：“RabbitMQ？”“Kafka？”“RocketMQ？”。</p><h3 id="_1、什么是消息队列" tabindex="-1"><a class="header-anchor" href="#_1、什么是消息队列" aria-hidden="true">#</a> 1、什么是消息队列？</h3><p>消息队列看作是一个存放消息的容器，当我们需要使用消息的时候，直接从容器中取出消息供自己使用即可。</p><p>消息队列是分布式系统中重要的组件之一。使用消息队列主要是为了通过<strong>异步处理提高系统性能和削峰、降低系统耦合性</strong>。</p><p>我们知道队列 Queue 是一种先进先出的数据结构，所以消费消息时也是按照顺序来消费的。</p><h3 id="_2、为什么要用消息队列" tabindex="-1"><a class="header-anchor" href="#_2、为什么要用消息队列" aria-hidden="true">#</a> 2、为什么要用消息队列？</h3><p>使用消息队列能为我们的系统带来下面三点好处：</p><ol><li><strong>通过异步处理提高系统性能（减少响应所需时间）。</strong></li><li><strong>削峰/限流</strong></li><li><strong>降低系统耦合性。</strong></li></ol><blockquote><p>通过异步处理提高系统性能（减少响应所需时间）</p></blockquote><p>将用户的请求数据存储到消息队列之后就立即返回结果。随后，系统再对消息进行消费。</p><p>因为用户请求数据写入消息队列之后就立即返回给用户了，但是请求数据在后续的业务校验、写数据库等操作中可能失败。因此，<strong>使用消息队列进行异步处理之后，需要适当修改业务流程进行配合</strong>，比如用户在提交订单之后，订单数据写入消息队列，不能立即返回用户订单提交成功，需要在消息队列的订单消费者进程真正处理完该订单之后，甚至出库后，再通过电子邮件或短信通知用户订单成功，以免交易纠纷。这就类似我们平时手机订火车票和电影票。</p><blockquote><p>削峰/限流</p></blockquote><p>先将短时间高并发产生的事务消息存储在消息队列中，然后后端服务再慢慢根据自己的能力去消费这些消息，这样就避免直接把后端服务打垮掉。</p><p>举例：在电子商务一些秒杀、促销活动中，合理使用消息队列可以有效抵御促销活动刚开始大量订单涌入对系统的冲击。如下图所示：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201519841.png" alt="image-20220620151924733" loading="lazy"></p><blockquote><p>降低系统耦合性</p></blockquote><p>使用消息队列还可以降低系统耦合性。我们知道如果模块之间不存在直接调用，那么新增模块或者修改模块就对其他模块影响较小，这样系统的可扩展性无疑更好一些。还是直接上图吧：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201519564.png" alt="image-20220620151951466" loading="lazy"></p><p>生产者（客户端）发送消息到消息队列中去，接受者（服务端）处理消息，需要消费的系统直接去消息队列取消息进行消费即可而不需要和其他系统有耦合，这显然也提高了系统的扩展性。</p><p><strong>消息队列使用发布-订阅模式工作，消息发送者（生产者）发布消息，一个或多个消息接受者（消费者）订阅消息。</strong> 从上图可以看到<strong>消息发送者（生产者）和消息接受者（消费者）之间没有直接耦合</strong>，消息发送者将消息发送至分布式消息队列即结束对消息的处理，消息接受者从分布式消息队列获取该消息后进行后续处理，并不需要知道该消息从何而来。<strong>对新增业务，只要对该类消息感兴趣，即可订阅该消息，对原有系统和业务没有任何影响，从而实现网站业务的可扩展性设计</strong>。</p><p>消息接受者对消息进行过滤、处理、包装后，构造成一个新的消息类型，将消息继续发送出去，等待其他消息接受者订阅该消息。因此基于事件（消息对象）驱动的业务架构可以是一系列流程。</p><p>另外，为了避免消息队列服务器宕机造成消息丢失，会将成功发送到消息队列的消息存储在消息生产者服务器上，等消息真正被消费者服务器处理后才删除消息。在消息队列服务器宕机后，生产者服务器会选择分布式消息队列服务器集群中的其他服务器发布消息。</p><p><strong>备注：</strong> 不要认为消息队列只能利用发布-订阅模式工作，只不过在解耦这个特定业务环境下是使用发布-订阅模式的。除了发布-订阅模式，还有点对点订阅模式（一个消息只有一个消费者），我们比较常用的是发布-订阅模式。另外，这两种消息模型是 JMS 提供的，AMQP 协议还提供了 5 种消息模型。</p><h3 id="_3、使用消息队列带来的一些问题" tabindex="-1"><a class="header-anchor" href="#_3、使用消息队列带来的一些问题" aria-hidden="true">#</a> 3、使用消息队列带来的一些问题</h3><p><strong>系统可用性降低：</strong> 系统可用性在某种程度上降低，为什么这样说呢？在加入 MQ 之前，你不用<strong>考虑消息丢失或者说 MQ 挂掉等等</strong>的情况，但是，引入 MQ 之后你就需要去考虑了！</p><p><strong>系统复杂性提高：</strong> 加入 MQ 之后，你需要<strong>保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性</strong>等等问题！</p><p><strong>一致性问题：</strong> 我上面讲了消息队列可以实现异步，消息队列带来的异步确实可以提高系统响应速度。但是，<strong>万一消息的真正消费者并没有正确消费消息怎么办？这样就会导致数据不一致的情况了</strong>!</p><h3 id="_4、jms-vs-amqp" tabindex="-1"><a class="header-anchor" href="#_4、jms-vs-amqp" aria-hidden="true">#</a> 4、JMS VS AMQP</h3><h4 id="_1、jms-产品有-activemq" tabindex="-1"><a class="header-anchor" href="#_1、jms-产品有-activemq" aria-hidden="true">#</a> 1、JMS（产品有：ActiveMQ）</h4><p>JMS（JAVA Message Service,java 消息服务）是 java 的消息服务，JMS 的客户端之间可以通过 JMS 服务进行异步的消息传输。<strong>JMS（JAVA Message Service，Java 消息服务）API 是一个消息服务的标准或者说是规范</strong>，允许应用程序组件基于 JavaEE 平台创建、发送、接收和读取消息。它使分布式通信耦合度更低，消息服务更加可靠以及异步性。</p><p>实际产品：ActiveMQ 就是基于 JMS 规范实现的。</p><blockquote><p>两种消息模型</p></blockquote><p>① 点到点（P2P）模型</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201533364.png" alt="image-20220620153329272" loading="lazy"></p><p>使用队列（Queue）作为消息通信载体；满足\生产者与消费者模式，一条消息只能被一个消费者使用，未被消费的消息在队列中保留直到被消费或超时。比如：我们生产者发送 100 条消息的话，两个消费者来消费一般情况下两个消费者会按照消息发送的顺序各自消费一半（也就是你一个我一个的消费。）</p><p>② 发布/订阅（Pub/Sub）模型</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201534331.png" alt="image-20220620153412241" loading="lazy"></p><p>发布订阅模型（Pub/Sub） 使用主题（Topic）作为消息通信载体，类似于广播模式；发布者发布一条消息，该消息通过主题传递给所有的订阅者，在一条消息广播之后才订阅的用户则是收不到该条消息的。</p><blockquote><p>五种不同的消息正文格式</p></blockquote><p>JMS 定义了五种不同的消息正文格式，以及调用的消息类型，允许你发送并接收以一些不同形式的数据，提供现有消息格式的一些级别的兼容性。</p><ul><li>StreamMessage -- Java 原始值的数据流</li><li>MapMessage--一套名称-值对</li><li>TextMessage--一个字符串对象</li><li>ObjectMessage--一个序列化的 Java 对象</li><li>BytesMessage--一个字节的数据流</li></ul><h4 id="_2、amqp-产品有-rabbitmq" tabindex="-1"><a class="header-anchor" href="#_2、amqp-产品有-rabbitmq" aria-hidden="true">#</a> 2、AMQP（产品有：<strong>RabbitMQ</strong> ）</h4><p>AMQP，即 Advanced Message Queuing Protocol，一个提供统一消息服务的应用层标准 <strong>高级消息队列协议</strong>（二进制应用层协议），是应用层协议的一个开放标准，为面向消息的中间件设计，兼容 JMS。基于此协议的客户端与消息中间件可传递消息，并不受客户端/中间件同产品，不同的开发语言等条件的限制。</p><p>RabbitMQ 就是基于 AMQP 协议实现的。</p><h4 id="_3、jms-vs-amqp" tabindex="-1"><a class="header-anchor" href="#_3、jms-vs-amqp" aria-hidden="true">#</a> 3、JMS vs AMQP</h4><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206201536968.png" alt="image-20220620153625864" loading="lazy"></p><h4 id="_4、总结" tabindex="-1"><a class="header-anchor" href="#_4、总结" aria-hidden="true">#</a> 4、总结</h4><p>AMQP 为消息定义了线路层（wire-level protocol）的协议，而 JMS 所定义的是 API 规范。在 Java 体系中，多个 client 均可以通过 JMS 进行交互，不需要应用修改代码，但是其对跨平台的支持较差。而 AMQP 天然具有跨平台、跨语言特性。</p><p>JMS 支持 TextMessage、MapMessage 等复杂的消息类型；而 AMQP 仅支持 byte[] 消息类型（复杂的类型可序列化后发送）。</p><p>由于 Exchange 提供的路由算法，AMQP 可以提供多样化的路由方式来传递消息到消息队列，而 JMS 仅支持 队列 和 主题/订阅 方式两种。</p><h3 id="_5、常见的消息队列机制" tabindex="-1"><a class="header-anchor" href="#_5、常见的消息队列机制" aria-hidden="true">#</a> 5、常见的消息队列机制</h3><table><thead><tr><th>对比方向</th><th style="text-align:left;">概要</th></tr></thead><tbody><tr><td>吞吐量</td><td style="text-align:left;">万级的 ActiveMQ 和 RabbitMQ 的吞吐量（ActiveMQ 的性能最差）要比 十万级甚至是百万级的 RocketMQ 和 Kafka 低一个数量级。</td></tr><tr><td>可用性</td><td style="text-align:left;">都可以实现高可用。ActiveMQ 和 RabbitMQ 都是基于主从架构实现高可用性。RocketMQ 基于分布式架构。 kafka 也是分布式的，一个数据多个副本，少数机器宕机，不会丢失数据，不会导致不可用</td></tr><tr><td>时效性</td><td style="text-align:left;">RabbitMQ 基于 erlang 开发，所以并发能力很强，性能极其好，延时很低，达到微秒级。其他三个都是 ms 级。</td></tr><tr><td>功能支持</td><td style="text-align:left;">除了 Kafka，其他三个功能都较为完备。 Kafka 功能较为简单，主要支持简单的 MQ 功能，在大数据领域的实时计算以及日志采集被大规模使用，是事实上的标准</td></tr><tr><td>消息丢失</td><td style="text-align:left;">ActiveMQ 和 RabbitMQ 丢失的可能性非常低， RocketMQ 和 Kafka 理论上不会丢失。</td></tr></tbody></table><h3 id="总结-4" tabindex="-1"><a class="header-anchor" href="#总结-4" aria-hidden="true">#</a> 总结</h3><p>ActiveMQ 的社区算是比较成熟，但是较目前来说，ActiveMQ 的性能比较差，而且版本迭代很慢，不推荐使用。</p><p><strong>RabbitMQ</strong> 在**吞吐量方面虽然稍逊于 Kafka 和 RocketMQ ，但是由于它基于 erlang 开发，所以并发能力很强，性能极其好，延时很低，达到微秒级。**但是也因为 RabbitMQ 基于 erlang 开发，所以国内很少有公司有实力做 erlang 源码级别的研究和定制。如果业务场景对并发量要求不是太高（十万级、百万级），那这四种消息队列中，RabbitMQ 一定是你的首选。如果是大数据领域的实时计算、日志采集等场景，<strong>用 Kafka 是业内标准的，绝对没问题，社区活跃度很高</strong>，绝对不会黄，何况几乎是全世界这个领域的事实性规范。</p><p><code>RocketMQ</code> 阿里出品，Java 系开源项目，源代码我们可以直接阅读，然后可以定制自己公司的 MQ，并且 RocketMQ 有阿里巴巴的实际业务场景的实战考验。RocketMQ 社区活跃度相对较为一般，不过也还可以，文档相对来说简单一些，然后接口这块不是按照标准 JMS 规范走的有些系统要迁移需要修改大量代码。还有就是阿里出台的技术，你得做好这个技术万一被抛弃，社区黄掉的风险，那如果你们公司有技术实力我觉得用 RocketMQ 挺好的。</p><p><code>Kafka</code> (大数据)的特点其实很明显，就是仅仅提供较少的核心功能，但是提供超高的吞吐量，ms 级的延迟，极高的可用性以及可靠性，而且分布式可以任意扩展。**同时 kafka 最好是支撑较少的 topic 数量即可，保证其超高吞吐量。kafka 唯一的一点劣势是有可能消息重复消费，那么对数据准确性会造成极其轻微的影响，**<strong>在大数据领域中以及日志采集中，这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集。</strong></p><h1 id="高可用" tabindex="-1"><a class="header-anchor" href="#高可用" aria-hidden="true">#</a> 高可用</h1><p>限流：</p><ul><li>限流算法：固定窗口计数器、滑动窗口、漏桶算法、令牌桶算法。</li><li>应用：单机限流（Guava ）、分布式限流（中间件Sentinel 或者使用 Redis 、网关层Spring Cloud Gateway）。</li></ul><p>降级、熔断：</p><ul><li>降级的目的在于应对系统自身的故障，而熔断的目的在于应对当前系统依赖的外部系统或者第三方系统的故障。</li><li>应用：SpringCloud的Hystrix、Resilience4J、Sentinel、Spring Retry。</li></ul><p>超时、重试机制：</p><ul><li>超时包含连接超时、读取超时。</li><li>重试机制：重试机制一般配合超时机制一起使用，指的是多次发送相同的请求来避免瞬态故障和偶然性故障。</li><li>重试幂等性考虑：我们在执行用户购买课程的请求的时候需要判断一下用户是否已经购买过。这样的话，就不会因为重试的问题导致重复购买了。</li></ul><p>集群：相同的服务部署多份，避免单点故障。</p><p>灾备设计、异地多活：</p><ul><li><strong>备份</strong> ： 将系统所产生的的所有重要数据多备份几份。</li><li><strong>容灾</strong> ： 在异地建立两个完全相同的系统。当某个地方的系统突然挂掉，整个应用系统可以切换到另一个，这样系统就可以正常提供服务了。</li><li><strong>异地多活</strong> 描述的是将服务部署在异地并且服务同时对外提供服务。和传统的灾备设计的最主要区别在于“多活”，即所有站点都是同时在对外提供服务的。异地多活是为了应对突发状况比如火灾、地震等自然或者人为灾害。</li></ul><h2 id="性能测试" tabindex="-1"><a class="header-anchor" href="#性能测试" aria-hidden="true">#</a> 性能测试</h2><p><strong>指标</strong>：响应时间、并发数、吞吐量、性能计数器。</p><ul><li>响应时间：系统能同时处理请求的数目即同时提交请求的用户数目。</li><li>qps与tps：如，访问一个页面会请求服务器2次，一次访问，产生一个“T”，产生2个“Q”。</li></ul><p><strong>测试方式</strong>：性能测试、负载测试、压力测试、稳定性测试</p><p><strong>工具</strong>：</p><p>后端：</p><ol><li>Jmeter ：Apache JMeter 是 JAVA 开发的性能测试工具。</li><li>LoadRunner：一款商业的性能测试工具。</li><li>Galtling ：一款基于Scala 开发的高性能服务器性能测试工具。</li><li>ab ：全称为 Apache Bench 。Apache 旗下的一款测试工具，非常实用。</li></ol><p>前端：</p><ol><li>Fiddler：抓包工具，它可以修改请求的数据，甚至可以修改服务器返回的数据，功能非常强大，是Web 调试的利器。</li><li>HttpWatch: 可用于录制HTTP请求信息的工具。</li></ol><p><strong>常见的性能优化策略</strong>：</p><ol><li>系统是否需要缓存？</li><li>系统架构本身是不是就有问题？</li><li>系统是否存在死锁的地方？</li><li>系统是否存在内存泄漏？（Java 的自动回收内存虽然很方便，但是，有时候代码写的不好真的会造成内存泄漏）</li><li>数据库索引使用是否合理？</li><li>......</li></ol><hr><p>整理者：长路 时间：2022.6.20</p></div><!----><footer class="page-meta"><div class="meta-item edit-link"><a href="https://gitee.com/changluJava/personal-reading/edit/main/demo/src/zh/interview/分布式与高性能高可用.md" rel="noopener noreferrer" target="_blank" aria-label="编辑此页" class="nav-link label"><!--[--><svg xmlns="http://www.w3.org/2000/svg" class="icon edit-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="edit icon"><path d="M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"></path><path d="M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"></path></svg><!--]-->编辑此页<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span><!----></a></div><!----><!----></footer><nav class="page-nav"><a href="/zh/interview/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6.html" class="nav-link prev" aria-label="主流框架"><div class="hint"><span class="arrow left"></span>上一页</div><div class="link"><span class="icon iconfont icon-lock" style=""></span>主流框架</div></a><a href="/zh/interview/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F.html" class="nav-link next" aria-label="操作系统"><div class="hint">下一页<span class="arrow right"></span></div><div class="link">操作系统<span class="icon iconfont icon-lock" style=""></span></div></a></nav><div class="giscus-wrapper input-top" style="display:block;"><div style="text-align:center">Loading...</div></div><!----><!--]--></main><!--]--><footer class="footer-wrapper"><div class="footer">默认页脚</div><div class="copyright">Copyright © 2022 长路</div></footer><!--]--></div><!--]--><!----><!--]--></div>
    <script type="module" src="/assets/app.e732cc94.js" defer></script>
  </body>
</html>
